【问题标题】:Append values to query string将值附加到查询字符串
【发布时间】:2013-01-09 04:19:38
【问题描述】:

我在列表中有一组类似于以下网址的网址

我已设法使用以下代码获取查询字符串:

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

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

但它只返回像这样的参数 idserverlocation等,基于提供的URL。

我需要向现有查询字符串添加/附加值。

以网址为例:

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

我需要更改查询字符串参数的值:

action=login1

attempts=11

如您所见,我为每个值附加了“1”。我需要从其中包含不同查询字符串的字符串中获取一组 URL,并在末尾为每个参数添加一个值,然后再次将它们添加到列表中。

【问题讨论】:

    标签: c# parameters query-string


    【解决方案1】:

    您可以使用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"
    

    【讨论】:

    • 从我的示例中可以看出,您可以为参数使用变量名。这正是它的作用:它将 2 个参数附加到我在此处硬编码的现有 url,但它们完全可以是动态的。
    • 我们在分配参数值时不应该使用HttpUtility.UrlEncode()吗?
    • @UserControl,不,HttpUtility.ParseQueryString 方法返回一个特殊的 NameValueCollection 实现,当您设置一个值时,它已经在后台处理了这个问题。
    • 这对 System.Web 有依赖,真是令人遗憾:/
    • 值得注意的是,这种方法可能会导致国际化问题,因为特殊字符将在 query.ToString() 方法中转换为它们的 unicode 等效项。
    【解决方案2】:

    我已将Darin's answer 包装成一个可重复使用的扩展方法。

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

    我希望这会有所帮助!

    【讨论】:

    【解决方案3】:

    提供的答案与相对 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)));
        }
      }
    

    【讨论】:

    • 不幸的是,此解决方案不适用于使用云 .NET 的 ASP.NET 5,因为 HttpUtility 似乎不可用。但否则这是一个很好的解决方案。见stackoverflow.com/questions/29992848/…
    • "Add_to_query_string_dictionary_when_url_contains_hash_and_no_query_string_should_add_values" 应该测试 URL 变为 domain.com/test?param1=val1&param2=val2#div
    • 请反省一下,您是否最好使用 uri.AbsoluteUri 而不是 uri.ToString() 因为讨厌的未转义效果。
    • 加法:uri.AbsoluteUri 抛出,如果 uri 不是绝对的!
    【解决方案4】:

    以下解决方案适用于 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&amp;param2=value2&amp;param3=value3

    【讨论】:

    • 使用字典作为查询键和值的存储的唯一问题是查询字符串可以具有具有不同值的重复键。我相信对 ASP.NET 站点的请求会将其解析为该键的值数组。
    【解决方案5】:

    我喜欢 Bjorn 的回答,但是他提供的解决方案具有误导性,因为该方法会更新现有参数,而不是在不存在时添加它。为了使其更安全,我在下面对其进行了调整。

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

    【讨论】:

    • 我确实对代码进行了边际编辑,我没有提供它(OP 提供了)......但是有什么区别?
    • if / else 不是必需的,在所有情况下都只需执行query[paramName] = paramValue;。它存在,它将被覆盖。如果不存在,将创建密钥。
    【解决方案6】:

    结束所有 URL 查询字符串编辑问题

    经过大量的辛勤工作和摆弄 Uri 类和其他解决方案,这是我的 string 扩展方法来解决我的问题。

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

    【讨论】:

    • 我不鼓励人们使用原始的strings 来表示这样的 URL,因为已经为此目的存在 Uri 类。要么使用它,要么在缺少功能时创建一个全新的抽象。
    【解决方案7】:

    请注意,您可以添加来自 Microsoft 的 Microsoft.AspNetCore.WebUtilities nuget 包,然后使用它来将值附加到查询字符串:

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

    【讨论】:

    • 从 ASP.NET Core 3.0 开始,WebUtilities 现在是 ASP.NET SDK 的一部分,因此不需要 nuget 包。
    • AddQueryString的问题是它会一直添加,如果已经有key就不会更新,而是创建重复的key,with不好
    • @Vencovsky 但是你可以使用QueryHelpers.ParseQuery检查它是否存在
    • @Vencovsky 我不同意;为什么要对同一个查询字符串使用同一参数的不同值进行多次传递?
    【解决方案8】:

    这更令人沮丧,因为现在(.net 5)MS 已将他们的许多(所有)方法标记为过时,这些方法采用字符串而不是 Uri

    无论如何,操纵相对Uris 的更好方法可能是给它想要的东西:

    var requestUri = new Uri("x://x").MakeRelativeUri(
       new UriBuilder("x://x") { Path = path, Query = query }.Uri);
    

    您可以使用其他答案来实际构建查询字符串。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多