Hängen Sie Werte an die Abfragezeichenfolge an

161

Ich habe URLs festgelegt, die den folgenden in einer Liste ähnlich sind

Ich habe es geschafft, die Abfragezeichenfolgen mit dem folgenden Code abzurufen:

myurl = longurl.Split('?');
NameValueCollection qs = HttpUtility.ParseQueryString(myurl [1]);

foreach (string lol in qs)
{
    // results will return
}

Es werden jedoch nur die Parameter wie ID , Server , Standort usw. basierend auf der angegebenen URL zurückgegeben.

Ich muss den vorhandenen Abfragezeichenfolgen Werte hinzufügen / anhängen.

Zum Beispiel mit der URL:

http://somesite.com/backup/index.php?action=login&attempt=1

Ich muss die Werte der Abfragezeichenfolgenparameter ändern:

action = login1

Versuche = 11

Wie Sie sehen können, habe ich für jeden Wert "1" angehängt. Ich muss eine Reihe von URLs aus einer Zeichenfolge mit verschiedenen Abfragezeichenfolgen abrufen und jedem Parameter am Ende einen Wert hinzufügen und sie erneut einer Liste hinzufügen.

DriverBoy
quelle

Antworten:

327

Sie können die HttpUtility.ParseQueryStringMethode und eine verwenden, UriBuilderdie eine gute Möglichkeit bietet, mit Abfragezeichenfolgenparametern zu arbeiten, ohne sich um Dinge wie Parsen, URL-Codierung, ... kümmern zu müssen:

string longurl = "http://somesite.com/news.php?article=1&lang=en";
var uriBuilder = new UriBuilder(longurl);
var query = HttpUtility.ParseQueryString(uriBuilder.Query);
query["action"] = "login1";
query["attempts"] = "11";
uriBuilder.Query = query.ToString();
longurl = uriBuilder.ToString();
// "http://somesite.com:80/news.php?article=1&lang=en&action=login1&attempts=11"
Darin Dimitrov
quelle
5
Wie Sie meinem Beispiel entnehmen können, können Sie Variablennamen für die Parameter verwenden. Und genau das macht es: Es hängt 2 Parameter an die vorhandene URL an, die ich hier fest codiert habe, aber sie könnten durchaus dynamisch sein.
Darin Dimitrov
1
Sollten wir nicht HttpUtility.UrlEncode()bei der Zuweisung von Parameterwerten verwenden?
UserControl
1
@UserControl, nein, die HttpUtility.ParseQueryStringMethode gibt eine spezielle NameValueCollection-Implementierung zurück, die dies bereits hinter den Kulissen behandelt, wenn Sie einen Wert festlegen.
Darin Dimitrov
5
Schade, dass dies eine Abhängigkeit von System.Web hat: /
Pure.Krome
4
Es ist anzumerken, dass dieser Ansatz Probleme mit der Internationalisierung verursachen kann, da Sonderzeichen in der query.ToString () -Methode in ihre Unicode-Entsprechungen konvertiert werden.
Tezromania
104

Ich habe Darins Antwort in eine gut wiederverwendbare Erweiterungsmethode eingewickelt .

public static class UriExtensions
{
    /// <summary>
    /// Adds the specified parameter to the Query String.
    /// </summary>
    /// <param name="url"></param>
    /// <param name="paramName">Name of the parameter to add.</param>
    /// <param name="paramValue">Value for the parameter to add.</param>
    /// <returns>Url with added parameter.</returns>
    public static Uri AddParameter(this Uri url, string paramName, string paramValue)
    {
        var uriBuilder = new UriBuilder(url);
        var query = HttpUtility.ParseQueryString(uriBuilder.Query);
        query[paramName] = paramValue;
        uriBuilder.Query = query.ToString();

        return uriBuilder.Uri;
    }
}

Ich hoffe das hilft!

Brinkie
quelle
55

Die bereitgestellten Antworten haben Probleme mit relativen URLs, wie z. B. "/ some / path /". Dies ist eine Einschränkung der Klassen Uri und UriBuilder, die ziemlich schwer zu verstehen ist, da ich keinen Grund sehe, warum relative URLs problematisch wären wenn es um die Manipulation von Abfragen geht.

Hier ist eine Problemumgehung, die sowohl für absolute als auch für relative Pfade funktioniert, die in .NET 4 geschrieben und getestet wurden:

(kleine Anmerkung: Das sollte funktionieren auch in .NET 4.5, werden Sie nur ändern müssen , propInfo.GetValue(values, null)um propInfo.GetValue(values))

  public static class UriExtensions{
    /// <summary>
    ///     Adds query string value to an existing url, both absolute and relative URI's are supported.
    /// </summary>
    /// <example>
    /// <code>
    ///     // returns "www.domain.com/test?param1=val1&amp;param2=val2&amp;param3=val3"
    ///     new Uri("www.domain.com/test?param1=val1").ExtendQuery(new Dictionary&lt;string, string&gt; { { "param2", "val2" }, { "param3", "val3" } }); 
    /// 
    ///     // returns "/test?param1=val1&amp;param2=val2&amp;param3=val3"
    ///     new Uri("/test?param1=val1").ExtendQuery(new Dictionary&lt;string, string&gt; { { "param2", "val2" }, { "param3", "val3" } }); 
    /// </code>
    /// </example>
    /// <param name="uri"></param>
    /// <param name="values"></param>
    /// <returns></returns>
    public static Uri ExtendQuery(this Uri uri, IDictionary<string, string> values) {
      var baseUrl = uri.ToString();
      var queryString = string.Empty;
      if (baseUrl.Contains("?")) {
        var urlSplit = baseUrl.Split('?');
        baseUrl = urlSplit[0];
        queryString = urlSplit.Length > 1 ? urlSplit[1] : string.Empty;
      }

      NameValueCollection queryCollection = HttpUtility.ParseQueryString(queryString);
      foreach (var kvp in values ?? new Dictionary<string, string>()) {
        queryCollection[kvp.Key] = kvp.Value;
      }
      var uriKind = uri.IsAbsoluteUri ? UriKind.Absolute : UriKind.Relative;
      return queryCollection.Count == 0 
        ? new Uri(baseUrl, uriKind) 
        : new Uri(string.Format("{0}?{1}", baseUrl, queryCollection), uriKind);
    }

    /// <summary>
    ///     Adds query string value to an existing url, both absolute and relative URI's are supported.
    /// </summary>
    /// <example>
    /// <code>
    ///     // returns "www.domain.com/test?param1=val1&amp;param2=val2&amp;param3=val3"
    ///     new Uri("www.domain.com/test?param1=val1").ExtendQuery(new { param2 = "val2", param3 = "val3" }); 
    /// 
    ///     // returns "/test?param1=val1&amp;param2=val2&amp;param3=val3"
    ///     new Uri("/test?param1=val1").ExtendQuery(new { param2 = "val2", param3 = "val3" }); 
    /// </code>
    /// </example>
    /// <param name="uri"></param>
    /// <param name="values"></param>
    /// <returns></returns>
    public static Uri ExtendQuery(this Uri uri, object values) {
      return ExtendQuery(uri, values.GetType().GetProperties().ToDictionary
      (
          propInfo => propInfo.Name,
          propInfo => { var value = propInfo.GetValue(values, null); return value != null ? value.ToString() : null; }
      ));
    }
  }

Und hier ist eine Reihe von Unit-Tests, um das Verhalten zu testen:

  [TestFixture]
  public class UriExtensionsTests {
    [Test]
    public void Add_to_query_string_dictionary_when_url_contains_no_query_string_and_values_is_empty_should_return_url_without_changing_it() {
      Uri url = new Uri("http://www.domain.com/test");
      var values = new Dictionary<string, string>();
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("http://www.domain.com/test")));
    }

    [Test]
    public void Add_to_query_string_dictionary_when_url_contains_hash_and_query_string_values_are_empty_should_return_url_without_changing_it() {
      Uri url = new Uri("http://www.domain.com/test#div");
      var values = new Dictionary<string, string>();
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("http://www.domain.com/test#div")));
    }

    [Test]
    public void Add_to_query_string_dictionary_when_url_contains_no_query_string_should_add_values() {
      Uri url = new Uri("http://www.domain.com/test");
      var values = new Dictionary<string, string> { { "param1", "val1" }, { "param2", "val2" } };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("http://www.domain.com/test?param1=val1&param2=val2")));
    }

    [Test]
    public void Add_to_query_string_dictionary_when_url_contains_hash_and_no_query_string_should_add_values() {
      Uri url = new Uri("http://www.domain.com/test#div");
      var values = new Dictionary<string, string> { { "param1", "val1" }, { "param2", "val2" } };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("http://www.domain.com/test#div?param1=val1&param2=val2")));
    }

    [Test]
    public void Add_to_query_string_dictionary_when_url_contains_query_string_should_add_values_and_keep_original_query_string() {
      Uri url = new Uri("http://www.domain.com/test?param1=val1");
      var values = new Dictionary<string, string> { { "param2", "val2" } };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("http://www.domain.com/test?param1=val1&param2=val2")));
    }

    [Test]
    public void Add_to_query_string_dictionary_when_url_is_relative_contains_no_query_string_should_add_values() {
      Uri url = new Uri("/test", UriKind.Relative);
      var values = new Dictionary<string, string> { { "param1", "val1" }, { "param2", "val2" } };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("/test?param1=val1&param2=val2", UriKind.Relative)));
    }

    [Test]
    public void Add_to_query_string_dictionary_when_url_is_relative_and_contains_query_string_should_add_values_and_keep_original_query_string() {
      Uri url = new Uri("/test?param1=val1", UriKind.Relative);
      var values = new Dictionary<string, string> { { "param2", "val2" } };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("/test?param1=val1&param2=val2", UriKind.Relative)));
    }

    [Test]
    public void Add_to_query_string_dictionary_when_url_is_relative_and_contains_query_string_with_existing_value_should_add_new_values_and_update_existing_ones() {
      Uri url = new Uri("/test?param1=val1", UriKind.Relative);
      var values = new Dictionary<string, string> { { "param1", "new-value" }, { "param2", "val2" } };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("/test?param1=new-value&param2=val2", UriKind.Relative)));
    }

    [Test]
    public void Add_to_query_string_object_when_url_contains_no_query_string_should_add_values() {
      Uri url = new Uri("http://www.domain.com/test");
      var values = new { param1 = "val1", param2 = "val2" };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("http://www.domain.com/test?param1=val1&param2=val2")));
    }


    [Test]
    public void Add_to_query_string_object_when_url_contains_query_string_should_add_values_and_keep_original_query_string() {
      Uri url = new Uri("http://www.domain.com/test?param1=val1");
      var values = new { param2 = "val2" };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("http://www.domain.com/test?param1=val1&param2=val2")));
    }

    [Test]
    public void Add_to_query_string_object_when_url_is_relative_contains_no_query_string_should_add_values() {
      Uri url = new Uri("/test", UriKind.Relative);
      var values = new { param1 = "val1", param2 = "val2" };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("/test?param1=val1&param2=val2", UriKind.Relative)));
    }

    [Test]
    public void Add_to_query_string_object_when_url_is_relative_and_contains_query_string_should_add_values_and_keep_original_query_string() {
      Uri url = new Uri("/test?param1=val1", UriKind.Relative);
      var values = new { param2 = "val2" };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("/test?param1=val1&param2=val2", UriKind.Relative)));
    }

    [Test]
    public void Add_to_query_string_object_when_url_is_relative_and_contains_query_string_with_existing_value_should_add_new_values_and_update_existing_ones() {
      Uri url = new Uri("/test?param1=val1", UriKind.Relative);
      var values = new { param1 = "new-value", param2 = "val2" };
      var result = url.ExtendQuery(values);
      Assert.That(result, Is.EqualTo(new Uri("/test?param1=new-value&param2=val2", UriKind.Relative)));
    }
  }
Moeri
quelle
Leider funktioniert diese Lösung nicht für ASP.NET 5 mit Cloud .NET, da HttpUtility nicht verfügbar zu sein scheint. Aber sonst ist es eine großartige Lösung. Siehe stackoverflow.com/questions/29992848/…
diegosasw
1
"Add_to_query_string_dictionary_when_url_contains_hash_and_no_query_string_should_add_values" sollte testen, ob die URL domain.com/test?param1=val1¶m2=val2#div
gliljas
Bitte überprüfen Sie, ob Sie nicht besser dran sind uri.AbsoluteUrials uri.ToString()wegen böser, nicht entkommender Effekte.
Nico
2
Ergänzung: uri.AbsoluteUriwirft, wenn der Uri nicht absolut ist!
Nico
19

Beachten Sie, dass Sie das Microsoft.AspNetCore.WebUtilitiesNuget-Paket von Microsoft hinzufügen und damit Werte an die Abfragezeichenfolge anhängen können:

QueryHelpers.AddQueryString(longurl, "action", "login1")
QueryHelpers.AddQueryString(longurl, new Dictionary<string, string> { { "action", "login1" }, { "attempts", "11" } });
Michael
quelle
3
Ab ASP.NET Core 3.0 ist WebUtilities jetzt Teil des ASP.NET SDK, sodass kein Nuget-Paket erforderlich ist.
user1069816
1
Das Problem mit AddQueryStringist, dass es immer hinzufügt, wenn es bereits den Schlüssel gibt, es wird nicht aktualisiert, sondern doppelte Schlüssel erstellen, mit ist schlecht
Vencovsky
11

Die folgende Lösung funktioniert für ASP.NET 5 (vNext) und verwendet die QueryHelpers-Klasse, um einen URI mit Parametern zu erstellen.

    public Uri GetUri()
    {
        var location = _config.Get("http://iberia.com");
        Dictionary<string, string> values = GetDictionaryParameters();

        var uri = Microsoft.AspNetCore.WebUtilities.QueryHelpers.AddQueryString(location, values);
        return new Uri(uri);
    }

    private Dictionary<string,string> GetDictionaryParameters()
    {
        Dictionary<string, string> values = new Dictionary<string, string>
        {
            { "param1", "value1" },
            { "param2", "value2"},
            { "param3", "value3"}
        };
        return values;
    }

Die Ergebnis-URI hätte http://iberia.com?param1=value1&param2=value2&param3=value3

diegosasw
quelle
Das einzige Problem bei der Verwendung eines Wörterbuchs als Speicher für Abfrageschlüssel und -werte besteht darin, dass Abfragezeichenfolgen doppelte Schlüssel mit unterschiedlichen Werten enthalten können. Ich glaube, dass eine Anforderung an eine ASP.NET-Site dies als Array von Werten für diesen einen Schlüssel analysiert.
Seth
2

Das Ende aller Probleme bei der Bearbeitung von URL-Abfragezeichenfolgen

Nach viel Mühe und Fummelei mit der Uri-Klasse und anderen Lösungen sind hier meine Methoden zur String- Erweiterung, um meine Probleme zu lösen.

using System;
using System.Collections.Specialized;
using System.Linq;
using System.Web;

public static class StringExtensions
{
    public static string AddToQueryString(this string url, params object[] keysAndValues)
    {
        return UpdateQueryString(url, q =>
        {
            for (var i = 0; i < keysAndValues.Length; i += 2)
            {
                q.Set(keysAndValues[i].ToString(), keysAndValues[i + 1].ToString());
            }
        });
    }

    public static string RemoveFromQueryString(this string url, params string[] keys)
    {
        return UpdateQueryString(url, q =>
        {
            foreach (var key in keys)
            {
                q.Remove(key);
            }
        });
    }

    public static string UpdateQueryString(string url, Action<NameValueCollection> func)
    {
        var urlWithoutQueryString = url.Contains('?') ? url.Substring(0, url.IndexOf('?')) : url;
        var queryString = url.Contains('?') ? url.Substring(url.IndexOf('?')) : null;
        var query = HttpUtility.ParseQueryString(queryString ?? string.Empty);

        func(query);

        return urlWithoutQueryString + (query.Count > 0 ? "?" : string.Empty) + query;
    }
}

quelle
1
Ich würde Leute davon abhalten, rohe strings zu verwenden, um URLs wie diese darzustellen, wenn man bedenkt, dass die UriKlasse zu diesem Zweck bereits existiert. Verwenden Sie dies entweder oder erstellen Sie eine brandneue Abstraktion, wenn Funktionen fehlen.
Julealgon
0

Ich mag Björns Antwort, aber die von ihm bereitgestellte Lösung ist irreführend, da die Methode einen vorhandenen Parameter aktualisiert, anstatt ihn hinzuzufügen, wenn er nicht vorhanden ist. Um ihn ein bisschen sicherer zu machen, habe ich ihn unten angepasst.

public static class UriExtensions
{
    /// <summary>
    /// Adds or Updates the specified parameter to the Query String.
    /// </summary>
    /// <param name="url"></param>
    /// <param name="paramName">Name of the parameter to add.</param>
    /// <param name="paramValue">Value for the parameter to add.</param>
    /// <returns>Url with added parameter.</returns>
    public static Uri AddOrUpdateParameter(this Uri url, string paramName, string paramValue)
    {
        var uriBuilder = new UriBuilder(url);
        var query = HttpUtility.ParseQueryString(uriBuilder.Query);

        if (query.AllKeys.Contains(paramName))
        {
            query[paramName] = paramValue;
        }
        else
        {
            query.Add(paramName, paramValue);
        }
        uriBuilder.Query = query.ToString();

        return uriBuilder.Uri;
    }
}
Stevieboy84
quelle
Ich habe den Code wirklich geringfügig bearbeitet, ich habe ihn nicht bereitgestellt (das OP hat es getan) ... was wird der Unterschied sein?
1
Das if / else ist nicht notwendig, nur query[paramName] = paramValue;in allen Fällen. Wenn es existiert, wird es überschrieben. Wenn es nicht existiert, wird der Schlüssel erstellt.
Richard
-4

Mein Ansatz ist für Anfänger sehr einfach:

// --> Prototype : https://ctrader.guru/?id=1#reload

    public static string AddGetParamToUrl(string url, string pname, string pvalue)
    { 

        pvalue = Uri.EscapeDataString(pvalue);

        if (url.IndexOf("?") > -1)
        {

            url = url.Replace("?", string.Format("?{0}={1}&", pname, pvalue));

        }
        else if (url.IndexOf("#") > -1)
        {

            url = url.Replace("#", string.Format("?{0}={1}#", pname, pvalue));

        }
        else
        {

            url = string.Format("{0}?{1}={2}", url, pname, pvalue);

        }

        return url;

    }
Leonardo Ciaccio
quelle