NameValueCollection到URL查询?


Answers:


113

简单地调用ToString()NameValueCollection将在返回名称值对name1=value1&name2=value2查询字符串支持的格式。请注意,NameValueCollection类型实际上并不支持此类型,并且建议这样做有误导性,但由于实际返回的内部类型在此行为有效,如下所述。

感谢@mjwills指出,该HttpUtility.ParseQueryString方法实际上返回一个内部HttpValueCollection对象而不是常规对象NameValueCollection尽管文档指定了NameValueCollection)。在HttpValueCollection使用时自动编码查询字符串ToString(),所以没有必要写一个程序,通过收集循环和使用UrlEncode方法。期望的结果已经返回。

有了结果,您可以将其附加到URL并重定向:

var nameValues = HttpUtility.ParseQueryString(Request.QueryString.ToString());
string url = Request.Url.AbsolutePath + "?" + nameValues.ToString();
Response.Redirect(url);

当前使用a的唯一方法HttpValueCollection是使用ParseQueryString上面显示的方法(当然,不是反射)。由于要求将此类公开发布Connect问题已被关闭,状态为“无法修复”,因此看起来这不会改变。

顺便说一句,你可以调用AddSet以及Remove对方法nameValues,以追加之前修改任何查询字符串的项目。如果您对此感兴趣,请参阅我对另一个问题的答复


3
我不确定nameValues.ToString()是否会正确转义。

9
实际上,您可以只使用qs.ToString()。这是因为qs不是NameValueCollection(尽管具有HttpUtility.ParseQueryString的方法签名)。它实际上是一个称为System.Web.HttpValueCollection的私有类型(您可以使用qs.GetType()进行检查)。NameValueCollection.ToString()不对URL进行编码,但是HttpValueCollection.ToString()对。因此,完全不需要使用StringBuilder。
mjwills

8
我先前的评论可能不正确。HttpValueCollection不调用HttpUtility.UrlEncode-而是调用HttpUtility.UrlEncodeUnicode(tinyurl.com/HttpValue)。这是一个问题,因为两者在处理某些字符(例如é)的方式上有所不同。有关UrlEncodeUnicode的新文档似乎建议不应使用它-msdn.microsoft.com/en-us/library/…
mjwills 2012年

42
NameValueCollection.ToString()返回System.Collections.Specialized.NameValueCollection字符串(从字面上看),而不是“以querystring ready格式的名称/值对”。
伊戈尔·布雷耶克

5
@IgorBrejc,谢谢,我已经更新了我的帖子,说它误导说NameValueCollection返回期望的格式。MSDN文档也具有误导性,因为它说虽然ParseQueryString返回了“ a”(NameValueCollection尽管实际上是)HttpValueCollection,但是调用ToString()它应该返回预期的格式,如其余答案所述。
艾哈迈德·玛吉德

66
string q = String.Join("&",
             nvc.AllKeys.Select(a => a + "=" + HttpUtility.UrlEncode(nvc[a])));

该答案是唯一可以正确处理url参数中编码的答案。荣誉!
ConfusedAboutCPP 2014年

7
也是唯一一个回答字面问题有关NameValueCollections,而不是涉及所提供的方案HttpValueCollection,而不是
drzaus

4
如果同一键有多个值,我认为这将无法正常工作。
马丁·布朗

3
@MartinBrown是正确的,事实并非如此。以下是适用于多值名称值集合的方法:uriBuilder.Query + = String.Join(“&”,parameters.AllKeys.SelectMany(key =>(parameters.GetValues(key)?? Enumerable.Empty <string>())。Select( val => String.Concat(key,“ =”,WebUtility.UrlEncode(val))))));
Alex Marshall'Mar

3
您也应该对密钥进行编码: string q = String.Join("&", nvc.AllKeys.Select(a => HttpUtility.UrlEncode(a) + "=" + HttpUtility.UrlEncode(nvc[a])));
SoftDev

20

制作一个使用几个循环的扩展方法。我更喜欢这种解决方案,因为它可读(无linq),不需要System.Web.HttpUtility,并且可以处理重复的键。

public static string ToQueryString(this NameValueCollection nvc)
{
    if (nvc == null) return string.Empty;

    StringBuilder sb = new StringBuilder();

    foreach (string key in nvc.Keys)
    {
        if (string.IsNullOrWhiteSpace(key)) continue;

        string[] values = nvc.GetValues(key);
        if (values == null) continue;

        foreach (string value in values)
        {
            sb.Append(sb.Length == 0 ? "?" : "&");
            sb.AppendFormat("{0}={1}", Uri.EscapeDataString(key), Uri.EscapeDataString(value));
        }
    }

    return sb.ToString();
}

var queryParams = new NameValueCollection()
{
    { "order_id", "0000" },
    { "item_id", "1111" },
    { "item_id", "2222" },
    { null, "skip entry with null key" },
    { "needs escaping", "special chars ? = &" },
    { "skip entry with null value", null }
};

Console.WriteLine(queryParams.ToQueryString());

输出量

?order_id=0000&item_id=1111&item_id=2222&needs%20escaping=special%20chars%20%3F%20%3D%20%26

16

这应该可以在没有太多代码的情况下工作:

NameValueCollection nameValues = HttpUtility.ParseQueryString(String.Empty);
nameValues.Add(Request.QueryString);
// modify nameValues if desired
var newUrl = "/page?" + nameValues;

这个想法是用来HttpUtility.ParseQueryString生成一个空的type集合HttpValueCollection。此类是NameValueCollection被标记为的子类,internal因此您的代码无法轻松创建其实例。

有趣的HttpValueCollection是,该ToString方法会为您处理编码。通过利用该NameValueCollection.Add(NameValueCollection)方法,您可以将现有的查询字符串参数添加到新创建的对象中,而不必先将Request.QueryString集合转换为url编码的字符串,然后将其解析回集合。

此技术也可以作为扩展方法公开:

public static string ToQueryString(this NameValueCollection nameValueCollection)
{
    NameValueCollection httpValueCollection = HttpUtility.ParseQueryString(String.Empty);
    httpValueCollection.Add(nameValueCollection);
    return httpValueCollection.ToString();
}

7

实际上,您也应该对键进行编码,而不仅仅是值。

string q = String.Join("&",
nvc.AllKeys.Select(a => $"{HttpUtility.UrlEncode(a)}={HttpUtility.UrlEncode(nvc[a])}"));

3

简短的回答是用.ToString()NameValueCollection与原始URL相结合。

但是,我想指出一些事情:

你不能使用HttpUtility.ParseQueryStringRequest.RawUrl。该ParseQueryString()方法正在寻找这样的值:?var=value&var2=value2

如果你想获得NameValueCollection的的QueryString参数,只需要使用Request.QueryString()

var nv = Request.QueryString;

要重建URL,只需使用nv.ToString()。

string url = String.Format("{0}?{1}", Request.Path, nv.ToString());

如果您尝试解析url字符串而不是使用Request对象useUriHttpUtility.ParseQueryString方法。

Uri uri = new Uri("<THE URL>");
var nv = HttpUtility.ParseQueryString(uri.Query);
string url = String.Format("{0}?{1}", uri.AbsolutePath, nv.ToString());

5
就像对已接受答案的评论指出(并在此基本重复)一样,Request.QueryString实际上不是a NameValueCollection,它是特殊的派生词HttpValueCollection,这就是为什么.ToString起作用的原因;定期雷士你必须使用类似这样的回答,而不是
drzaus

3

因为一个键NameValueCollection可以有多个值,所以如果您担心查询字符串的格式(因为它将以逗号分隔的值而不是“数组符号”的形式返回),则可以考虑以下内容。

var nvc = new NameValueCollection();
nvc.Add("key1", "val1");
nvc.Add("key2", "val2");
nvc.Add("empty", null);
nvc.Add("key2", "val2b");

变成:key1=val1&key2[]=val2&empty&key2[]=val2b而不是key1=val1&key2=val2,val2b&empty

string qs = string.Join("&", 
    // "loop" the keys
    nvc.AllKeys.SelectMany(k => {
        // "loop" the values
        var values = nvc.GetValues(k);
        if(values == null) return new[]{ k };
        return nvc.GetValues(k).Select( (v,i) => 
            // 'gracefully' handle formatting
            // when there's 1 or more values
            string.Format(
                values.Length > 1
                    // pick your array format: k[i]=v or k[]=v, etc
                    ? "{0}[]={1}"
                    : "{0}={1}"
                , k, HttpUtility.UrlEncode(v), i)
        );
    })
);

或者如果您不太喜欢Linq ...

string qs = nvc.ToQueryString(); // using...

public static class UrlExtensions {
    public static string ToQueryString(this NameValueCollection nvc) {
        return string.Join("&", nvc.GetUrlList());
    }

    public static IEnumerable<string> GetUrlList(this NameValueCollection nvc) {
        foreach(var k in nvc.AllKeys) {
            var values = nvc.GetValues(k);
            if(values == null)  { yield return k; continue; }
            for(int i = 0; i < values.Length; i++) {
                yield return
                // 'gracefully' handle formatting
                // when there's 1 or more values
                string.Format(
                    values.Length > 1
                        // pick your array format: k[i]=v or k[]=v, etc
                        ? "{0}[]={1}"
                        : "{0}={1}"
                    , k, HttpUtility.UrlEncode(values[i]), i);
            }
        }
    }
}

正如已经在评论中指出的那样,除了该答案以外,其他大多数答案都针对场景(Request.QueryStringHttpValueCollection,“不是” a NameValueCollection)而不是字面问题。

更新:解决了评论中的空值问题。


好主意,但是您的linq代码不会检查null。如何保证nvc.GetValues(k)不返回null?
CodingTT

@ tianxu0836好的电话; 我假设一个没有值的键将返回一个空字符串,但是猜不到。
drzaus


1

我总是使用UriBuilder将带有查询字符串的网址转换回有效且正确编码的网址。

var url = "http://my-link.com?foo=bar";

var uriBuilder = new UriBuilder(url);
var query = HttpUtility.ParseQueryString(uriBuilder.Query);
query.Add("yep", "foo&bar");

uriBuilder.Query = query.ToString();
var result = uriBuilder.ToString();

// http://my-link.com:80/?foo=bar&yep=foo%26bar

0

就像@Atchitutchuk建议的那样,您可以在ASP.NET Core中使用QueryHelpers.AddQueryString:

    public string FormatParameters(NameValueCollection parameters)
    {
        var queryString = "";
        foreach (var key in parameters.AllKeys)
        {
            foreach (var value in parameters.GetValues(key))
            {
                queryString = QueryHelpers.AddQueryString(queryString, key, value);
            }
        };

        return queryString.TrimStart('?');
    }

0

这对我有用:

public ActionResult SetLanguage(string language = "fr_FR")
        {            
            Request.UrlReferrer.TryReadQueryAs(out RouteValueDictionary parameters);            
            parameters["language"] = language;            
            return RedirectToAction("Index", parameters);            
        }

-1

您可以使用。

var ur = new Uri("/page",UriKind.Relative);

如果此nv是字符串类型,则可以附加到uri first参数。喜欢

var ur2 = new Uri("/page?"+nv.ToString(),UriKind.Relative);

+ nv ...无法正确逃脱。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.