当从代码中调用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,我没有找到一个,这让我想到了以下问题:

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


当前回答

结合顶部的答案创建一个匿名对象版本:

var queryString = HttpUtility2.BuildQueryString(new
{
    key2 = "value2",
    key1 = "value1",
});

这会产生这样的结果:

key2 = value2&key1 = value1

代码如下:

public static class HttpUtility2
{
    public static string BuildQueryString<T>(T obj)
    {
        var queryString = HttpUtility.ParseQueryString(string.Empty);

        foreach (var property in TypeDescriptor.GetProperties(typeof(T)).Cast<PropertyDescriptor>())
        {
            var value = (property.GetValue(obj) ?? "").ToString();
            queryString.Add(property.Name, value);
        }

        return queryString.ToString();
    }
}

其他回答

查询字符串可以通过以下方式添加到URL:

创建名称值集合对象 将查询字符串项及其值添加到此对象 将此名称值集合对象编码为下面链接中提供的代码的url

https://blog.codingnovice.com/blog

public ActionResult Create()
{
    //declaring name value collection object
    NameValueCollection collection = new NameValueCollection();

    //adding new value to the name value collection object
    collection.Add("Id1", "wwe323");
    collection.Add("Id2", "454w");
    collection.Add("Id3", "tyt5656");
    collection.Add("Id4", "343wdsd");

    //generating query string
    string url = GenerateQueryString(collection);

    return View();
}

private string GenerateQueryString(NameValueCollection collection)
{
    var querystring = (
        from key in collection.AllKeys
        from value in collection.GetValues(key)
        select string.Format("{0}={1}",
            HttpUtility.UrlEncode(key),
            HttpUtility.UrlEncode(value))
    ).ToArray();
    return "?" + string.Join("&", querystring);
}

未经测试,但我认为沿着这些路线的东西会工作得很好

public class QueryString
{
    private Dictionary<string,string> _Params = new Dictionary<string,string>();

    public overide ToString()
    {
        List<string> returnParams = new List<string>();

        foreach (KeyValuePair param in _Params)
        {
            returnParams.Add(String.Format("{0}={1}", param.Key, param.Value));
        }

        // return String.Format("?{0}", String.Join("&", returnParams.ToArray())); 

        // credit annakata
        return "?" + String.Join("&", returnParams.ToArray());
    }

    public void Add(string key, string value)
    {
        _Params.Add(key, HttpUtility.UrlEncode(value));
    }
}

QueryString query = new QueryString();

query.Add("param1", "value1");
query.Add("param2", "value2");

return query.ToString();

如果你仔细观察,QueryString属性是一个NameValueCollection。当我做了类似的事情,我通常对序列化和反序列化感兴趣,所以我的建议是建立一个NameValueCollection,然后传递给:

using System.Linq;
using System.Web;
using System.Collections.Specialized;

private string ToQueryString(NameValueCollection nvc)
{
    var array = (
        from key in nvc.AllKeys
        from value in nvc.GetValues(key)
            select string.Format(
                "{0}={1}",
                HttpUtility.UrlEncode(key),
                HttpUtility.UrlEncode(value))
        ).ToArray();
    return "?" + string.Join("&", array);
}

我想在LINQ中也有一种非常优雅的方式来做到这一点……

这里有很多很好的答案,但对于使用现代c#的人来说,这可能是一个很好的实用程序类。

public class QueryParamBuilder
{
    private readonly Dictionary<string, string> _fields = new();
    public QueryParamBuilder Add(string key, string value)
    {
        _fields.Add(key, value);
        return this;
    }
    public string Build()
    {
        return $"?{String.Join("&", _fields.Select(pair => $"{HttpUtility.UrlEncode(pair.Key)}={HttpUtility.UrlEncode(pair.Value)}"))}";
    }
    public static QueryParamBuilder New => new();
}

我在这里使用了一个内部Dictionary,因为字典在内部是可枚举的键值对,这使得遍历它们比NameValueCollection容易得多。

那么查询字符串本身就是一个简单的带有连接的插值字符串。

另外,我为构造函数提供了一个静态接口,使构造新的构造器非常容易,并且只允许一个公开的方法Add来添加新的查询参数值。最后,使用Build()终止链以实际获得最终字符串。

下面是它用法的一个例子

var queryString = QueryParamBuilder.New
     .Add("id", "0123")
     .Add("value2", 1234.ToString())
     .Add("valueWithSpace","value with spa12!@#@!ce")
     .Build();

结果和预期的一样

?id=0123&value2=1234&valueWithSpace=value+with+spa12!%40%23%40!ce

希望你们中的一些人会觉得这个很好很优雅。

在dotnet核心QueryHelpers.AddQueryString()将接受一个IDictionary<string,string>的键值对。为了节省一些内存分配和CPU周期,您可以使用SortedList<,>而不是Dictionary<,>,并按照排序顺序添加适当的容量和项…

var queryParams = new SortedList<string,string>(2);
queryParams.Add("abc", "val1");
queryParams.Add("def", "val2");

string requestUri = QueryHelpers.AddQueryString("https://localhost/api", queryParams);