如何在 C# 中为 URL 构建查询字符串?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/829080/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 03:17:59  来源:igfitidea点击:

How to build a query string for a URL in C#?

c#.neturlquery-string

提问by Boaz

A common task when calling web resources from a code is building a query string to including all the necessary parameters. While by all means no rocket science, there are some nifty details you need to take care of like, appending an &if not the first parameter, encoding the parameters etc.

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

The code to do it is very simple, but a bit tedious:

执行它的代码非常简单,但有点乏味:

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")); }
}

This is such a common task one would expect a utility class to exist that makes it more elegant and readable. Scanning MSDN, I failed to find one—which brings me to the following question:

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

What is the most elegant clean way you know of doing the above?

你知道的最优雅的清洁方式是什么?

采纳答案by annakata

If you look under the hood the QueryString property is a NameValueCollection. When I've done similar things I've usually been interested in serialising AND deserialising so my suggestion is to build a NameValueCollection up and then pass to:

如果您深入了解,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);
}

I imagine there's a super elegant way to do this in LINQ too...

我想在 LINQ 中也有一种超级优雅的方式来做到这一点......

回答by Nick Allen

Untested, but I think something along these lines would work quite nicely

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

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();

回答by JonnyBoats

EDIT - as pointed out in the comments, this is not the way to go.

编辑 - 正如评论中指出的那样,这不是要走的路。

There is such a class - the URI Class. "Provides an object representation of a uniform resource identifier (URI) and easy access to the parts of the URI." (Microsoft docs).

有这样一个类——URI 类。“提供统一资源标识符 (URI) 的对象表示和对 URI 各部分的轻松访问。” (微软文档)。

The following example creates an instance of the Uri class and uses it to create a WebRequest instance.

以下示例创建 Uri 类的实例并使用它来创建 WebRequest 实例。

C# example

C# 示例

Uri siteUri = new Uri("http://www.contoso.com/");

Uri siteUri = new Uri(" http://www.contoso.com/");

WebRequest wr = WebRequest.Create(siteUri);

WebRequest wr = WebRequest.Create(siteUri);

Check it out, there are lots of methods on this class.

看看吧,这个类有很多方法。

回答by Martin Harris

A quick extension method based version:

基于快速扩展方法的版本:

class Program
{
    static void Main(string[] args)
    {
        var parameters = new List<KeyValuePair<string, string>>
                             {
                                 new KeyValuePair<string, string>("A", "AValue"),
                                 new KeyValuePair<string, string>("B", "BValue")
                             };

        string output = "?" + string.Join("&", parameters.ConvertAll(param => param.ToQueryString()).ToArray());
    }
}

public static class KeyValueExtensions
{
    public static string ToQueryString(this KeyValuePair<string, string> obj)
    {
        return obj.Key + "=" + HttpUtility.UrlEncode(obj.Value);
    }
}

You could use a where clause to select which parameters get added to the string.

您可以使用 where 子句来选择将哪些参数添加到字符串中。

回答by Igal Tabachnik

I answered a similar questiona while ago. Basically, the best way would be to use the class HttpValueCollection, which ASP.NET's Request.QueryStringproperty actually is, unfortunately it is internal in the .NET framework. You could use Reflector to grab it (and place it into your Utils class). This way you could manipulate the query string like a NameValueCollection, but with all the url encoding/decoding issues taken care for you.

不久前我回答了一个类似的问题。基本上,最好的方法是使用类HttpValueCollection,ASP.NET 的Request.QueryString属性实际上是它,不幸的是它在 .NET 框架中是内部的。您可以使用 Reflector 来抓取它(并将其放入您的 Utils 类中)。通过这种方式,您可以像 NameValueCollection 一样操作查询字符串,但所有 url 编码/解码问题都会为您处理。

HttpValueCollectionextends NameValueCollection, and has a constructor that takes an encodedquery string (ampersands and question marks included), and it overrides a ToString()method to later rebuild the query string from the underlying collection.

HttpValueCollectionextends NameValueCollection,并有一个构造函数,它接受一个编码的查询字符串(包括与号和问号),它覆盖了一个ToString()方法,以便稍后从基础集合中重建查询字符串。

Example:

例子:

  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

回答by LukeH

How about creating extension methods that allow you to add the parameters in a fluent style like this?

如何创建允许您像这样以流畅的样式添加参数的扩展方法?

string a = "http://www.somedomain.com/somepage.html"
    .AddQueryParam("A", "TheValueOfA")
    .AddQueryParam("B", "TheValueOfB")
    .AddQueryParam("Z", "TheValueOfZ");

string b = new StringBuilder("http://www.somedomain.com/anotherpage.html")
    .AddQueryParam("A", "TheValueOfA")
    .AddQueryParam("B", "TheValueOfB")
    .AddQueryParam("Z", "TheValueOfZ")
    .ToString(); 

Here's the overload that uses a string:

这是使用 a 的重载string

public static string AddQueryParam(
    this string source, string key, string value)
{
    string delim;
    if ((source == null) || !source.Contains("?"))
    {
        delim = "?";
    }
    else if (source.EndsWith("?") || source.EndsWith("&"))
    {
        delim = string.Empty;
    }
    else
    {
        delim = "&";
    }

    return source + delim + HttpUtility.UrlEncode(key)
        + "=" + HttpUtility.UrlEncode(value);
}

And here's the overload that uses a StringBuilder:

这是使用 a 的重载StringBuilder

public static StringBuilder AddQueryParam(
    this StringBuilder source, string key, string value)
{
    bool hasQuery = false;
    for (int i = 0; i < source.Length; i++)
    {
        if (source[i] == '?')
        {
            hasQuery = true;
            break;
        }
    }

    string delim;
    if (!hasQuery)
    {
        delim = "?";
    }
    else if ((source[source.Length - 1] == '?')
        || (source[source.Length - 1] == '&'))
    {
        delim = string.Empty;
    }
    else
    {
        delim = "&";
    }

    return source.Append(delim).Append(HttpUtility.UrlEncode(key))
        .Append("=").Append(HttpUtility.UrlEncode(value));
}

回答by Thomas Bratt

Assuming that you want to reduce dependencies to other assemblies and to keep things simple, you can do:

假设您想减少对其他程序集的依赖并保持简单,您可以执行以下操作:

var sb = new System.Text.StringBuilder();

sb.Append("a=" + HttpUtility.UrlEncode("TheValueOfA") + "&");
sb.Append("b=" + HttpUtility.UrlEncode("TheValueOfB") + "&");
sb.Append("c=" + HttpUtility.UrlEncode("TheValueOfC") + "&");
sb.Append("d=" + HttpUtility.UrlEncode("TheValueOfD") + "&");

sb.Remove(sb.Length-1, 1); // Remove the final '&'

string result = sb.ToString();

This works well with loops too. The final ampersand removal needs to go outside of the loop.

这也适用于循环。最后的&符号删除需要在循环之外。

Note that the concatenation operator is used to improve readability. The cost of using it compared to the cost of using a StringBuilder is minimal (I think Jeff Atwoodposted something on this topic).

请注意,连接运算符用于提高可读性。与使用 StringBuilder 的成本相比,使用它的成本是最低的(我认为Jeff Atwood在这个主题上发表了一些东西)。

回答by Mike Cole

I added the following method to my PageBase class.

我将以下方法添加到我的 PageBase 类中。

protected void Redirect(string url)
    {
        Response.Redirect(url);
    }
protected void Redirect(string url, NameValueCollection querystrings)
    {
        StringBuilder redirectUrl = new StringBuilder(url);

        if (querystrings != null)
        {
            for (int index = 0; index < querystrings.Count; index++)
            {
                if (index == 0)
                {
                    redirectUrl.Append("?");
                }

                redirectUrl.Append(querystrings.Keys[index]);
                redirectUrl.Append("=");
                redirectUrl.Append(HttpUtility.UrlEncode(querystrings[index]));

                if (index < querystrings.Count - 1)
                {
                    redirectUrl.Append("&");
                }
            }
        }

        this.Redirect(redirectUrl.ToString());
    }

To call:

致电:

NameValueCollection querystrings = new NameValueCollection();    
querystrings.Add("language", "en");
querystrings.Add("id", "134");
this.Redirect("http://www.mypage.com", querystrings);

回答by John Bledsoe

You can create a new writeable instance of HttpValueCollectionby calling System.Web.HttpUtility.ParseQueryString(string.Empty), and then use it as any NameValueCollection. Once you have added the values you want, you can call ToStringon the collection to get a query string, as follows:

您可以HttpValueCollection通过调用创建一个新的可写实例System.Web.HttpUtility.ParseQueryString(string.Empty),然后将其用作 any 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

The HttpValueCollectionis internal and so you cannot directly construct an instance. However, once you obtain an instance you can use it like any other NameValueCollection. Since the actual object you are working with is an HttpValueCollection, calling ToString method will call the overridden method on HttpValueCollection, which formats the collection as a URL-encoded query string.

HttpValueCollection是内部的,所以你不能直接构造一个实例。但是,一旦您获得了一个实例,您就可以像使用任何其他NameValueCollection. 由于您使用的实际对象是HttpValueCollection,因此调用 ToString 方法将调用 上的重写方法HttpValueCollection,该方法将集合格式化为 URL 编码的查询字符串。

After searching SO and the web for an answer to a similar issue, this is the most simple solution I could find.

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

.NET Core

.NET 核心

If you're working in .NET Core, you can use the Microsoft.AspNetCore.WebUtilities.QueryHelpersclass, which simplifies this greatly.

如果您在 .NET Core 中工作,则可以使用Microsoft.AspNetCore.WebUtilities.QueryHelpers该类,这大大简化了这一过程。

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

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

Sample Code:

示例代码:

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));

回答by blak3r

Here is an implementation which uses very basic language features. It's part of a class which we have to port and maintain in Objective C so we choose to have more lines of code but easier to port and understand by a programmer that isn't very familiar with C#.

这是一个使用非常基本的语言功能的实现。它是我们必须在 Objective C 中移植和维护的类的一部分,因此我们选择有更多的代码行,但更容易被不太熟悉 C# 的程序员移植和理解。

        /// <summary>
        /// Builds a complete http url with query strings.
        /// </summary>
        /// <param name="pHostname"></param>
        /// <param name="pPort"></param>
        /// <param name="pPage">ex "/index.html" or index.html</param>
        /// <param name="pGetParams">a Dictionary<string,string> collection containing the key value pairs.  Pass null if there are none.</param>
        /// <returns>a string of the form: http://[pHostname]:[pPort/[pPage]?key1=val1&key2=val2...</returns>

  static public string buildURL(string pHostname, int pPort, string pPage, Dictionary<string,string> pGetParams)
        {
            StringBuilder sb = new StringBuilder(200);
            sb.Append("http://");
            sb.Append(pHostname);
            if( pPort != 80 ) {
                sb.Append(pPort);
            }
            // Allows page param to be passed in with or without leading slash.
            if( !pPage.StartsWith("/") ) {
                sb.Append("/");
            }
            sb.Append(pPage);

            if (pGetParams != null && pGetParams.Count > 0)
            {
                sb.Append("?");
                foreach (KeyValuePair<string, string> kvp in pGetParams)
                {
                    sb.Append(kvp.Key);
                    sb.Append("=");
                    sb.Append( System.Web.HttpUtility.UrlEncode(kvp.Value) );
                    sb.Append("&");
                }
                sb.Remove(sb.Length - 1, 1); // Remove the final '&'
            }
            return sb.ToString();
        }