如何在.NET中将查询字符串解析为NameValueCollection

191

我想将类似于 p1=6&p2=7&p3=8 的字符串解析为 NameValueCollection

当你无法访问 Page.Request 对象时,最优雅的方法是什么?

19个回答

371

这个有内置的.NET工具来完成:HttpUtility.ParseQueryString

// C#
NameValueCollection qscoll = HttpUtility.ParseQueryString(querystring);
' VB.NET
Dim qscoll As NameValueCollection = HttpUtility.ParseQueryString(querystring)

您可能需要用new Uri(fullUrl).Query替换querystring


26
Omar,对我来说在ASP.NET 4上并没有起作用,它返回了一个键为"http://stackoverflow.com?para"而不是"para"的结果。所以我正在使用HttpUtility.ParseQueryString(new Uri(fullUrl).Query),这对我来说可以正确地工作。 - Michael
2
qscoll["p1"],qscoll["p2"]和qscoll["p3"] - SMUsamaShah
5
在桌面应用程序中使用ParseQueryString是一个非常糟糕的想法,因为它没有包含在客户端配置文件中。为了只使用一个简单的方法,就需要在客户端计算机上安装100 M的额外库,这是不必要的。然而,似乎微软没有更好的想法。唯一的方法是实现自己的方法或使用开源实现。 - Vitalii
2
VASoftOnline:在这种情况下,您可以使用ParseQueryString的Mono实现:https://github.com/mono/mono/blob/master/mcs/class/System.Web/System.Web/HttpUtility.cs,该许可证为MIT X11:https://github.com/mono/mono/blob/master/LICENSE。 - sw.
4
HttpUtility.ParseQueryString是我的建议,但在 HttpUtility.ParseQueryString("&X=1&X=2&X=3") 的情况下,结果会变成....X=1,2,3...。虽然同名参数出现多次并不常见,但需要支持控制器参数,例如int[], IEnumerable<int>等(此类参数可能用于支持多个复选框),请参见MS网站中的“相同查询字符串变量的多个实例合并为一个条目”。手工编写该方法可能是您唯一的选择。 - dunxz
显示剩余6条评论

45

HttpUtility.ParseQueryString 只要你在 Web 应用程序中,或者不介意引入 System.Web 依赖项,它就能工作。另一种实现方式如下:

NameValueCollection queryParameters = new NameValueCollection();
string[] querySegments = queryString.Split('&');
foreach(string segment in querySegments)
{
   string[] parts = segment.Split('=');
   if (parts.Length > 0)
   {
      string key = parts[0].Trim(new char[] { '?', ' ' });
      string val = parts[1].Trim();

      queryParameters.Add(key, val);
   }
}

5
要确保可以调用parts[1],你应该检查parts.Length是否大于1。 - Alexandru Pupsa
3
如果没有值怎么办?例如,查询字符串可能看起来像 ?foo=1&barHttpUtility 将解析为 { key = null, value = "bar" } - Thomas Levesque
与HttpUtility.ParseQueryString相比,这个更好,因为它不会解码值(因此base64编码的值得以保留)。 - CRice
1
实际上,您仍然需要允许值'=',例如&code=A0SYw34Hj/m++lH0s7r0l/yg6GWdymzSCbI2zOn3V4o=将删除最后一个'='字符。我重写了您的代码的一部分,以获取'='的第一个索引,并对键和值进行子字符串处理以解决此问题(而不是使用split)。 - CRice
@ThomasLevesque 哇,你说得对!ParseQueryString()将“bar”解析为值而不是键。有Bug! - Suncat2000
显示剩余2条评论

32
许多答案都提供了自定义示例,因为已接受答案依赖于System.Web。从Microsoft.AspNet.WebApi.Client NuGet包中,还可以使用UriExtensions.ParseQueryString方法:
var uri = new Uri("https://dev59.com/5HVD5IYBdhLWcg3wKYL-#22167748?p1=6&p2=7&p3=8");
NameValueCollection query = uri.ParseQueryString();

所以,如果你想避免 System.Web 依赖并且不想自己编写,这是一个不错的选择。

3
在System.Net.Http命名空间中存在一个作为扩展的相同函数(请参见我下面的答案),不需要另一个完整的依赖项... - Jerod Venema
1
@jvenema 你是从哪里添加 System.Net.Http.Formatting 依赖的?我相信只有通过添加 Microsoft.AspNet.WebApi.Client NuGet 包才能提供它。 - James Skimming
有点疯狂,竟然需要添加一个全新的NuGet包作为依赖项,特别是其中带有AspNet的名称,这似乎只适用于在服务器上部署(在我的情况下,我需要它用于移动/桌面Xamarin应用程序...) - knocte

21

我想要去除对System.Web的依赖,这样我就可以解析ClickOnce部署的查询字符串,同时将先决条件限制为“仅客户端框架子集”。

我喜欢rp的答案。我添加了一些额外的逻辑。

public static NameValueCollection ParseQueryString(string s)
    {
        NameValueCollection nvc = new NameValueCollection();

        // remove anything other than query string from url
        if(s.Contains("?"))
        {
            s = s.Substring(s.IndexOf('?') + 1);
        }

        foreach (string vp in Regex.Split(s, "&"))
        {
            string[] singlePair = Regex.Split(vp, "=");
            if (singlePair.Length == 2)
            {
                nvc.Add(singlePair[0], singlePair[1]);
            }
            else
            {
                // only one key with no value specified in query string
                nvc.Add(singlePair[0], string.Empty);
            }
        }

        return nvc;
    }

这对于Windows Phone非常有帮助,你只需要用“SortedDictionnary<string,string>”替换“NameValueCollection”。 - Mike Bryant
5
在将NameValueCollection替换为Dictionary时要小心 - 它们并不相同!查询字符串支持具有相同值的多个键,NameValueCollection也是如此。 - Matt DeKrey

9

3
你是从哪里添加了System.Net.Http.Formatting依赖项的?我相信只有通过添加Microsoft.AspNet.WebApi.Client NuGet包才能提供它。 - James Skimming
哦,我的错。我猜它是随着自动安装的MVC框架一起安装的,所以我不需要添加任何额外的包(Program Files(x86)\ Microsoft ASP.NET \ ASP.NET MVC 4 \ Assemblies \ System.Net.Http.Formatting.dll)。 - Jerod Venema
System.Net.Formatting扩展在VS2019中是一个单独的列表或选项卡,与传统框架引用不同。 - Sql Surfer

7

我需要一个比现有 OLSC 查询提供的功能更加灵活的函数。

  • 值可能包含多个相等符号
  • 解码名称和值中的编码字符
  • 能够在客户端框架上运行
  • 能够在移动框架上运行。

这是我的解决方案:

Public Shared Function ParseQueryString(ByVal uri As Uri) As System.Collections.Specialized.NameValueCollection
    Dim result = New System.Collections.Specialized.NameValueCollection(4)
    Dim query = uri.Query
    If Not String.IsNullOrEmpty(query) Then
        Dim pairs = query.Substring(1).Split("&"c)
        For Each pair In pairs
            Dim parts = pair.Split({"="c}, 2)

            Dim name = System.Uri.UnescapeDataString(parts(0))
            Dim value = If(parts.Length = 1, String.Empty,
                System.Uri.UnescapeDataString(parts(1)))

            result.Add(name, value)
        Next
    End If
    Return result
End Function

也许在这个基础上加上 <Extension()> 也不是一个坏主意,可以为 Uri 本身添加这种功能。


4
如果你想避免使用 HttpUtility.ParseQueryString 需要依赖 System.Web,你可以使用在 System.Net.Http 中找到的 Uri 扩展方法 ParseQueryString。
请确保在项目中添加对 System.Net.Http 的引用(如果尚未添加)。
请注意,您必须将响应正文转换为有效的 Uri,以使 ParseQueryString(在 System.Net.Http 中)正常工作。
string body = "value1=randomvalue1&value2=randomValue2";

// "http://localhost/query?" is added to the string "body" in order to create a valid Uri.
string urlBody = "http://localhost/query?" + body;
NameValueCollection coll = new Uri(urlBody).ParseQueryString();

System.Net.Formatting扩展:VS2019将扩展引用与传统框架引用分开。 - Sql Surfer

4

3

我刚刚意识到,Web API Client有一个ParseQueryString扩展方法,可以在Uri上工作,并返回一个HttpValueCollection

var parameters = uri.ParseQueryString();
string foo = parameters["foo"];

System.Web.HttpUtility.ParseQueryString 还返回一个 HttpValueCollection(它在内部派生自 NameValueCollection)。两者都有同样的问题:它们无法正确解析裸键。 - Suncat2000

2
    private void button1_Click( object sender, EventArgs e )
    {
        string s = @"p1=6&p2=7&p3=8";
        NameValueCollection nvc = new NameValueCollection();

        foreach ( string vp in Regex.Split( s, "&" ) )
        {
            string[] singlePair = Regex.Split( vp, "=" );
            if ( singlePair.Length == 2 )
            {
                nvc.Add( singlePair[ 0 ], singlePair[ 1 ] );    
            }    
        }
    }

5
在HTTP中,分号也被允许用作参数分隔符,最好不要重复造轮子。 - Matthew Lock
当轮子坏了,最好是更换它。 - Suncat2000
我非常天真的回答让我想钻进洞里!这是“我用正则表达式解决了问题,现在我有两个问题”的完美定义。 - rp.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接