Добавить значения в строку запроса

162

У меня есть набор URL-адресов, аналогичных приведенным ниже в списке

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

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

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

Но он возвращает только такие параметры, как идентификатор , сервер , местоположение и т. Д. На основе предоставленного URL-адреса.

Что мне нужно, это добавить / добавить значения в существующие строки запроса.

Например с URL:

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

Мне нужно изменить значения параметров строки запроса:

действие = login1

Попытки = 11

Как видите, я добавил «1» для каждого значения. Мне нужно получить набор URL-адресов из строки с различными строками запроса и добавить значение к каждому параметру в конце и снова добавить их в список.

DriverBoy
источник

Ответы:

329

Вы можете использовать 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
Стоит отметить, что этот подход может вызвать проблемы с интернационализацией, так как специальные символы будут преобразованы в их эквиваленты Юникода в методе query.ToString ().
Tezromania
105

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

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

Надеюсь, это поможет!

Brinkie
источник
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)));
    }
  }
Moeri
источник
К сожалению, это решение не работает для 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пакет nuget от 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, поэтому пакет nuget не требуется.
user1069816
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

diegosasw
источник
Единственная проблема с использованием словаря в качестве хранилища ключей и значений запроса состоит в том, что строки запроса могут иметь дублирующиеся ключи с различными значениями. Я считаю, что запрос к сайту ASP.NET анализирует это как массив значений для этого одного ключа.
Сет
2

Конец всем бедам редактирования строки запроса URL

После многих трудов и возни с классом Uri и другими решениями, вот мои методы расширения строк, чтобы решить мои проблемы.

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класс уже существует для этой цели. Или используйте это, или создайте новую абстракцию, если функции отсутствуют.
Jullealgon
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;
    }
}
Stevieboy84
источник
Я действительно сделал незначительное редактирование кода, я не предоставил его (ОП сделал) ... какая разница, хотя?
1
Если / не нужно, просто делайте 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;

    }
Леонардо Чаччо
источник