Dołącz wartości do ciągu zapytania

162

Mam zestaw adresów URL podobnych do poniższych na liście

Udało mi się uzyskać ciągi zapytań za pomocą następującego kodu:

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

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

Ale zwraca tylko parametry, takie jak identyfikator , serwer , lokalizacja i tak dalej, na podstawie podanego adresu URL.

Potrzebuję dodania / dołączenia wartości do istniejących ciągów zapytań.

Na przykład z adresem URL:

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

Muszę zmienić wartości parametrów ciągu zapytania:

action = login1

próby = 11

Jak widać, dodałem „1” do każdej wartości. Muszę pobrać zestaw adresów URL z ciągu zawierającego różne ciągi zapytań i dodać wartość do każdego parametru na końcu i ponownie dodać je do listy.

DriverBoy
źródło

Odpowiedzi:

328

Możesz użyć HttpUtility.ParseQueryStringmetody i metody, UriBuilderktóra zapewnia przyjemny sposób pracy z parametrami ciągu zapytania bez martwienia się o takie rzeczy, jak analizowanie, kodowanie adresu URL, ...:

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
źródło
5
Jak widać na moim przykładzie, można użyć nazw zmiennych dla parametrów. I to jest dokładnie to, co robi: dodaje 2 parametry do istniejącego adresu URL, który tutaj zakodowałem, ale mogą być doskonale dynamiczne.
Darin Dimitrov
1
Czy nie powinniśmy używać HttpUtility.UrlEncode()przy przypisywaniu wartości parametru?
UserControl,
1
@UserControl, nie, HttpUtility.ParseQueryStringmetoda zwraca specjalną implementację NameValueCollection, która już obsługuje to za kulisami, gdy ustawiasz wartość.
Darin Dimitrov,
5
Szkoda, że ​​ma to zależność od System.Web: /
Pure.Krome
4
Warto zauważyć, że takie podejście może powodować problemy z internacjonalizacją, ponieważ znaki specjalne zostaną przekonwertowane na ich odpowiedniki w Unicode w metodzie query.ToString ().
tezromania
104

Ja owinięty odpowiedź Darin do wielokrotnego użytku ładnie metodę rozszerzenia.

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;
    }
}

Mam nadzieję, że to pomoże!

Brinkie
źródło
55

Podane odpowiedzi mają problemy ze względnymi adresami URL, takimi jak „/ some / path /” Jest to ograniczenie klasy Uri i UriBuilder, które jest raczej trudne do zrozumienia, ponieważ nie widzę powodu, dla którego względne adresy URL byłyby problematyczne jeśli chodzi o manipulację zapytaniami.

Oto obejście, które działa zarówno dla ścieżek bezwzględnych, jak i względnych, napisanych i przetestowanych w .NET 4:

(mała uwaga: powinno to również działać w .NET 4.5, wystarczy zmienić propInfo.GetValue(values, null)na 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; }
      ));
    }
  }

A oto zestaw testów jednostkowych do testowania zachowania:

  [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
źródło
niestety to rozwiązanie nie działa dla ASP.NET 5 przy użyciu chmury .NET, ponieważ HttpUtility wydaje się być niedostępne. Ale inaczej jest to świetne rozwiązanie. Zobacz stackoverflow.com/questions/29992848/…
diegosasw
1
„Add_to_query_string_dictionary_when_url_contains_hash_and_no_query_string_should_add_values” powinno sprawdzać, czy adres URL
zmienia
Proszę sprawdzić, czy nie lepiej jest używać uri.AbsoluteUrizamiast z uri.ToString()powodu nieprzyjemnych efektów, które nie uciekają.
Nico
2
Dodatek: uri.AbsoluteUrirzuty, jeśli uri nie jest bezwzględne!
Nico
19

Uwaga, możesz dodać Microsoft.AspNetCore.WebUtilitiespakiet NuGet firmy Microsoft, a następnie użyć tego do dołączenia wartości do ciągu zapytania:

QueryHelpers.AddQueryString(longurl, "action", "login1")
QueryHelpers.AddQueryString(longurl, new Dictionary<string, string> { { "action", "login1" }, { "attempts", "11" } });
Michael
źródło
3
Od ASP.NET Core 3,0 WebUtilities jest teraz częścią zestawu ASP.NET SDK, więc nie jest potrzebny żaden pakiet NuGet.
user1069816
1
Problem AddQueryStringpolega na tym, że zawsze doda, jeśli jest już klucz, nie zaktualizuje, ale utworzy zduplikowane klucze, z jest zły
Vencovsky
11

Poniższe rozwiązanie działa w przypadku ASP.NET 5 (vNext) i używa klasy QueryHelpers do tworzenia identyfikatora URI z parametrami.

    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;
    }

Wynikowy identyfikator URI miałby http://iberia.com?param1=value1&param2=value2&param3=value3

diegosasw
źródło
Jedynym problemem związanym z używaniem słownika jako magazynu kluczy zapytań i wartości jest to, że ciągi zapytań mogą mieć zduplikowane klucze o różnych wartościach. Uważam, że żądanie do witryny ASP.NET analizuje to jako tablicę wartości dla tego jednego klucza.
Seth
2

Koniec wszystkich problemów związanych z edycją ciągu zapytania URL

Po wielu trudach i majstrowaniu przy klasie Uri i innych rozwiązaniach, oto moje metody rozszerzania ciągów , które rozwiązują moje problemy.

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;
    }
}

źródło
1
Zniechęcałbym ludzi do używania nieprzetworzonych znaków strings do reprezentowania takich adresów URL, biorąc pod uwagę, że Uriklasa już istnieje do tego celu. Użyj tego lub stwórz zupełnie nową abstrakcję, jeśli brakuje funkcji.
julealgon
0

Podoba mi się odpowiedź Bjorna, jednak podane przez niego rozwiązanie jest mylące, ponieważ metoda aktualizuje istniejący parametr, zamiast dodawać go, jeśli nie istnieje. Aby było trochę bezpieczniej, dostosowałem go poniżej.

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
źródło
Naprawdę dokonałem marginalnej edycji kodu, nie dostarczyłem go (OP zrobił) ... jaka będzie jednak różnica?
1
Jeśli / else nie jest konieczne, po prostu zrób to query[paramName] = paramValue;we wszystkich przypadkach. Jeśli istnieje, zostanie nadpisany. Jeśli nie istnieje, zostanie utworzony klucz.
Richard
-4

Moje podejście jest bardzo proste, dla początkujących:

// --> 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
źródło