当从代码中调用web资源时,一个常见的任务是构建一个包含所有必要参数的查询字符串。虽然这绝不是火箭科学,但有一些漂亮的细节需要注意,例如,如果不是第一个参数,则添加&,对参数进行编码等。

实现它的代码非常简单,但有点乏味:

StringBuilder SB = new StringBuilder();
if (NeedsToAddParameter A) 
{ 
  SB.Append("A="); SB.Append(HttpUtility.UrlEncode("TheValueOfA")); 
}

if (NeedsToAddParameter B) 
{
  if (SB.Length>0) SB.Append("&"); 
  SB.Append("B="); SB.Append(HttpUtility.UrlEncode("TheValueOfB")); }
}

这是一个非常常见的任务,人们希望存在一个实用工具类,使其更加优雅和可读。扫描MSDN,我没有找到一个,这让我想到了以下问题:

你所知道的最优雅干净的方法是什么?


当前回答

我刚才回答了一个类似的问题。基本上,最好的方法是使用HttpValueCollection类。净的请求。QueryString属性实际上是,不幸的是,它是。net框架内部的。 您可以使用Reflector来抓取它(并将其放入您的Utils类中)。通过这种方式,您可以像操作NameValueCollection一样操作查询字符串,但是需要考虑所有url编码/解码问题。

HttpValueCollection扩展了NameValueCollection,并有一个接受编码查询字符串(包括&和问号)的构造函数,它覆盖了ToString()方法,以便稍后从底层集合重新构建查询字符串。

例子:

  var coll = new HttpValueCollection();

  coll["userId"] = "50";
  coll["paramA"] = "A";
  coll["paramB"] = "B";      

  string query = coll.ToString(true); // true means use urlencode

  Console.WriteLine(query); // prints: userId=50&paramA=A&paramB=B

其他回答

下面的代码是从ToString的HttpValueCollection实现中截取的,通过ILSpy,它会给你一个name=value querystring。

不幸的是,HttpValueCollection是一个内部类,只有当你使用HttpUtility.ParseQueryString()时才会返回。我删除了它的所有视图状态部分,它默认编码:

public static class HttpExtensions
{
    public static string ToQueryString(this NameValueCollection collection)
    {
        // This is based off the NameValueCollection.ToString() implementation
        int count = collection.Count;
        if (count == 0)
            return string.Empty;

        StringBuilder stringBuilder = new StringBuilder();

        for (int i = 0; i < count; i++)
        {
            string text = collection.GetKey(i);
            text = HttpUtility.UrlEncodeUnicode(text);
            string value = (text != null) ? (text + "=") : string.Empty;
            string[] values = collection.GetValues(i);
            if (stringBuilder.Length > 0)
            {
                stringBuilder.Append('&');
            }
            if (values == null || values.Length == 0)
            {
                stringBuilder.Append(value);
            }
            else
            {
                if (values.Length == 1)
                {
                    stringBuilder.Append(value);
                    string text2 = values[0];
                    text2 = HttpUtility.UrlEncodeUnicode(text2);
                    stringBuilder.Append(text2);
                }
                else
                {
                    for (int j = 0; j < values.Length; j++)
                    {
                        if (j > 0)
                        {
                            stringBuilder.Append('&');
                        }
                        stringBuilder.Append(value);
                        string text2 = values[j];
                        text2 = HttpUtility.UrlEncodeUnicode(text2);
                        stringBuilder.Append(text2);
                    }
                }
            }
        }

        return stringBuilder.ToString();
    }
}

将这个类添加到项目中

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

public class QueryStringBuilder
{
    private readonly List<KeyValuePair<string, object>> _list;

    public QueryStringBuilder()
    {
        _list = new List<KeyValuePair<string, object>>();
    }

    public void Add(string name, object value)
    {
        _list.Add(new KeyValuePair<string, object>(name, value));
    }

    public override string ToString()
    {
        return String.Join("&", _list.Select(kvp => String.Concat(Uri.EscapeDataString(kvp.Key), "=", Uri.EscapeDataString(kvp.Value.ToString()))));
    }
}

像这样使用它:

var actual = new QueryStringBuilder {
    {"foo", 123},
    {"bar", "val31"},
    {"bar", "val32"}
};

actual.Add("a+b", "c+d");

actual.ToString(); // "foo=123&bar=val31&bar=val32&a%2bb=c%2bd"

奇怪的是没有人提到来自asp.net . core的QueryBuilder。

当你有一个重复键的查询,比如?filter=a&filter=b,它会很有用

var qb = new QueryBuilder();
qb.Add("filter", new string[] {"A", "B"});

然后你只需要将qb添加到URI中,它就会自动转换为字符串。

https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.extensions.querybuilder?view=aspnetcore-5.0

你可以通过调用system . web . httputiity . parsequerystring (string.Empty)来创建一个新的可写的HttpValueCollection实例,然后将它用作任何NameValueCollection。一旦你添加了你想要的值,你可以在集合上调用ToString来获得一个查询字符串,如下所示:

NameValueCollection queryString = System.Web.HttpUtility.ParseQueryString(string.Empty);

queryString.Add("key1", "value1");
queryString.Add("key2", "value2");

return queryString.ToString(); // Returns "key1=value1&key2=value2", all URL-encoded

HttpValueCollection是内部的,所以你不能直接构造一个实例。然而,一旦你获得了一个实例,你就可以像使用其他NameValueCollection一样使用它。因为你正在使用的实际对象是一个HttpValueCollection,调用ToString方法将调用HttpValueCollection上的重写方法,它将集合格式化为url编码的查询字符串。

在SO和网络上搜索类似问题的答案后,这是我能找到的最简单的解决方案。

net核心

如果你在。net Core中工作,你可以使用Microsoft.AspNetCore.WebUtilities.QueryHelpers类,它极大地简化了这一点。

https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.webutilities.queryhelpers

示例代码:

const string url = "https://customer-information.azure-api.net/customers/search/taxnbr";
var param = new Dictionary<string, string>() { { "CIKey", "123456789" } };

var newUrl = new Uri(QueryHelpers.AddQueryString(url, param));

与已接受的解决方案相同,但转换为“点”LINQ语法…

private string ToQueryString(NameValueCollection nvc)
{
    if (nvc == null) return String.Empty;
    var queryParams = 
          string.Join("&", nvc.AllKeys.Select(key => 
              string.Join("&", nvc.GetValues(key).Select(v => string.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(v))))));
    return "?" + queryParams;
}