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

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


当前回答

我的提供:

public static Uri AddQuery(this Uri uri, string name, string value)
{
    // this actually returns HttpValueCollection : NameValueCollection
    // which uses unicode compliant encoding on ToString()
    var query = HttpUtility.ParseQueryString(uri.Query);

    query.Add(name, value);

    var uriBuilder = new UriBuilder(uri)
    {
        Query = query.ToString()
    };

    return uriBuilder.Uri;
}

用法:

var uri = new Uri("http://stackoverflow.com").AddQuery("such", "method")
                                             .AddQuery("wow", "soFluent");

// http://stackoverflow.com?such=method&wow=soFluent

其他回答

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

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中也有一种非常优雅的方式来做到这一点……

我为我的剃刀项目写了一个助手,使用了其他答案的一些提示。

ParseQueryString业务是必要的,因为我们不允许篡改当前请求的QueryString对象。

@helper GetQueryStringWithValue(string key, string value) {
    var queryString = System.Web.HttpUtility.ParseQueryString(HttpContext.Current.Request.QueryString.ToString());
    queryString[key] = value;
    @Html.Raw(queryString.ToString())
}

我是这样使用的:

location.search = '?@Helpers.GetQueryStringWithValue("var-name", "var-value")';

如果希望它接受多个值,只需将参数更改为Dictionary,并将对添加到查询字符串中。

这与公认的答案完全相同,只是稍微更紧凑:

private string ToQueryString(NameValueCollection nvc)
{
    return "?" + string.Join("&", nvc.AllKeys.Select(k => string.Format("{0}={1}", 
        HttpUtility.UrlEncode(k), 
        HttpUtility.UrlEncode(nvc[k]))));
}

我使用了DSO提出的解决方案(在8月2日11日7:29回答),他的解决方案不需要使用HttpUtility。然而,根据Dotnetpearls上的一篇文章,使用Dictionary比使用NameValueCollection(在性能上)更快。下面是修改后的DSO解决方案,使用Dictionary代替NameValueCollection。

    public static Dictionary<string, string> QueryParametersDictionary()
    {
        var dictionary = new Dictionary<string, string>();
        dictionary.Add("name", "John Doe");
        dictionary.Add("address.city", "Seattle");
        dictionary.Add("address.state_code", "WA");
        dictionary.Add("api_key", "5352345263456345635");

        return dictionary;
    }

    public static string ToQueryString(Dictionary<string, string> nvc)
    {
        StringBuilder sb = new StringBuilder();

        bool first = true;

        foreach (KeyValuePair<string, string> pair in nvc)
        {
                if (!first)
                {
                    sb.Append("&");
                }

                sb.AppendFormat("{0}={1}", Uri.EscapeDataString(pair.Key), Uri.EscapeDataString(pair.Value));

                first = false;
        }

        return sb.ToString();
    }