Додайте значення до рядка запиту


162

У списку я встановив URL-адреси, подібні до наведених нижче

Мені вдалося отримати рядки запиту, використовуючи наступний код:

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

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

Але він повертає лише такі параметри, як id , сервер , місцеположення тощо, на основі наданої URL-адреси.

Що мені потрібно - це додати / додати значення до існуючих рядків запитів.

Наприклад, з URL-адресою:

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

Мені потрібно змінити значення параметрів рядка запиту:

дія = вхід1

спроби = 11

Як бачите, я додав "1" до кожного значення. Мені потрібно отримати набір URL-адрес із рядка з різними рядками запиту в них і додати значення до кожного параметра в кінці, і знову додати їх до списку.

Відповіді:


328

Ви можете скористатися HttpUtility.ParseQueryStringметодом, UriBuilderякий забезпечує хороший спосіб роботи з параметрами рядка запиту, не турбуючись про такі речі, як розбір, кодування 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"

5
Як видно з мого прикладу, ви можете використовувати імена змінних для параметрів. І саме це і робиться: він додає 2 параметри до існуючої URL-адреси, яку я жорстко закодував тут, але вони могли чудово бути динамічними.
Дарин Димитров

1
Чи не повинні ми використовуватись HttpUtility.UrlEncode()при призначенні значення параметра?
UserControl

1
@UserControl, ні, HttpUtility.ParseQueryStringметод повертає спеціальну ім'я NameValueCollection, яка вже обробляє це за кадром, коли ви встановлюєте значення.
Дарин Димитров

5
Баммер, що це має залежність від System.Web: /
Pure.Krome

4
Варто зазначити, що такий підхід може викликати проблеми з інтернаціоналізацією, оскільки спеціальні символи будуть перетворені в їхні еквіваленти unicode в методі query.ToString ().
тезроманія

104

Я переклав відповідь Даріна в метод багаторазового розширення.

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

Я сподіваюся, що це допомагає!


55

Надані відповіді мають питання щодо відносних URL-адрес, таких як "/ some / path /" Це обмеження для класів Uri та UriBuilder, яке досить важко зрозуміти, оскільки я не бачу жодної причини, чому відносні URL-адреси були б проблематичними. коли мова йде про маніпуляції із запитами.

Ось вирішення, яке працює як для абсолютних, так і відносних шляхів, записаних і перевірених у .NET 4:

(невелика примітка: це також має працювати в .NET 4.5, вам доведеться лише змінити propInfo.GetValue(values, null)на 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; }
      ));
    }
  }

Ось набір одиничних тестів для перевірки поведінки:

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

на жаль, це рішення не працює для ASP.NET 5 за допомогою хмари .NET, оскільки HttpUtility, здається, не доступний. Але це відмінне рішення інакше. Див stackoverflow.com/questions/29992848 / ...
diegosasw

1
"Add_to_query_string_dictionary_when_url_contains_hash_and_no_query_string_should_add_values" повинен перевірити, що URL-адреса стає domain.com/test?param1=val1¶m2=val2#div
gliljas

Будь ласка, зустрічі перевірити, чи вам не краще використовувати uri.AbsoluteUriзамість uri.ToString()неприємних наслідків, що не стикаються.
Ніко

2
Додавання: uri.AbsoluteUriкидає, якщо урі не абсолютна!
Ніко

19

Зауважте, що ви можете додати Microsoft.AspNetCore.WebUtilitiesпакунок із нульових значень від Microsoft, а потім використати це для додавання значень до рядка запиту:

QueryHelpers.AddQueryString(longurl, "action", "login1")
QueryHelpers.AddQueryString(longurl, new Dictionary<string, string> { { "action", "login1" }, { "attempts", "11" } });

3
Станом на ASP.NET Core 3.0 WebUtilities тепер є частиною пакету ASP.NET SDK, тому не потрібен нут-пакет.
користувач1069816

1
Проблема AddQueryStringполягає в тому, що він завжди додаватиме, якщо вже є ключ, він не оновлюватиметься, але створить повторювані ключі, при цьому погано
Венцовський

11

Наступне рішення працює для ASP.NET 5 (vNext), і він використовує клас QueryHelpers для створення URI з параметрами.

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

Результат URI мав би http://iberia.com?param1=value1&param2=value2&param3=value3


Єдина проблема використання словника як сховища ключів запитів та значень полягає в тому, що рядки запиту можуть мати повторювані ключі з різними значеннями. Я вважаю, що запит на сайт ASP.NET розбирає це як масив значень для цього одного ключа.
Сет

2

Кінець всім проблемам редагування рядків запитів URL-адрес

Після безлічі труднощів і перешкод з класом Урі та іншими рішеннями, ось мої методи розширення рядків для вирішення моїх проблем.

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

1
Я б заважав людям використовувати raw strings для представлення таких URL-адрес, враховуючи, що Uriклас вже існує для цієї мети. Або скористайтеся цим, або створіть абсолютно нову абстракцію, якщо відсутні функції.
julealgon

0

Мені подобається відповідь Бьорна, проте рішення, яке він пропонує, вводить в оману, оскільки метод оновлює існуючий параметр, а не додає його, якщо його немає. Щоб зробити його трохи безпечнішим, я адаптував його нижче.

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

Я дійсно зробив незначне редагування коду, я його не надав (ОП зробив) ... у чому різниця?

1
Якщо / else не потрібно, просто робіть query[paramName] = paramValue;у всіх випадках. Це воно існує, його буде замінено. Якщо його не існує, ключ буде створений.
Річард

-4

Мій підхід для початківців дуже простий:

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

    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.