C# 为 System.Net.HttpClient 构建查询字符串获取

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17096201/
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-10 08:34:28  来源:igfitidea点击:

Build query string for System.Net.HttpClient get

c#.nethttp

提问by NeoDarque

If I wish to submit a http get request using System.Net.HttpClient there seems to be no api to add parameters, is this correct?

如果我想使用 System.Net.HttpClient 提交 http get 请求,似乎没有添加参数的 api,这是正确的吗?

Is there any simple api available to build the query string that doesn't involve building a name value collection and url encoding those and then finally concatenating them? I was hoping to use something like RestSharp's api (i.e AddParameter(..))

是否有任何简单的 api 可用于构建不涉及构建名称值集合和 url 编码的查询字符串,然后最终连接它们?我希望使用类似 RestSharp 的 api(即 AddParameter(..))

采纳答案by Darin Dimitrov

If I wish to submit a http get request using System.Net.HttpClient there seems to be no api to add parameters, is this correct?

如果我想使用 System.Net.HttpClient 提交 http get 请求,似乎没有添加参数的 api,这是正确的吗?

Yes.

是的。

Is there any simple api available to build the query string that doesn't involve building a name value collection and url encoding those and then finally concatenating them?

是否有任何简单的 api 可用于构建不涉及构建名称值集合和 url 编码的查询字符串,然后最终连接它们?

Sure:

当然:

var query = HttpUtility.ParseQueryString(string.Empty);
query["foo"] = "bar<>&-baz";
query["bar"] = "bazinga";
string queryString = query.ToString();

will give you the expected result:

会给你预期的结果:

foo=bar%3c%3e%26-baz&bar=bazinga

You might also find the UriBuilderclass useful:

您可能还会发现UriBuilder该类很有用:

var builder = new UriBuilder("http://example.com");
builder.Port = -1;
var query = HttpUtility.ParseQueryString(builder.Query);
query["foo"] = "bar<>&-baz";
query["bar"] = "bazinga";
builder.Query = query.ToString();
string url = builder.ToString();

will give you the expected result:

会给你预期的结果:

http://example.com/?foo=bar%3c%3e%26-baz&bar=bazinga

that you could more than safely feed to your HttpClient.GetAsyncmethod.

您可以更安全地使用您的HttpClient.GetAsync方法。

回答by Mike Perrenoud

Darin offered an interesting and clever solution, and here is something that may be another option:

Darin 提供了一个有趣而聪明的解决方案,这里有一些可能是另一种选择:

public class ParameterCollection
{
    private Dictionary<string, string> _parms = new Dictionary<string, string>();

    public void Add(string key, string val)
    {
        if (_parms.ContainsKey(key))
        {
            throw new InvalidOperationException(string.Format("The key {0} already exists.", key));
        }
        _parms.Add(key, val);
    }

    public override string ToString()
    {
        var server = HttpContext.Current.Server;
        var sb = new StringBuilder();
        foreach (var kvp in _parms)
        {
            if (sb.Length > 0) { sb.Append("&"); }
            sb.AppendFormat("{0}={1}",
                server.UrlEncode(kvp.Key),
                server.UrlEncode(kvp.Value));
        }
        return sb.ToString();
    }
}

and so when using it, you might do this:

所以在使用它时,你可以这样做:

var parms = new ParameterCollection();
parms.Add("key", "value");

var url = ...
url += "?" + parms;

回答by Roman Ratskey

Or simply using my Uri extension

或者简单地使用我的 Uri 扩展

Code

代码

public static Uri AttachParameters(this Uri uri, NameValueCollection parameters)
{
    var stringBuilder = new StringBuilder();
    string str = "?";
    for (int index = 0; index < parameters.Count; ++index)
    {
        stringBuilder.Append(str + parameters.AllKeys[index] + "=" + parameters[index]);
        str = "&";
    }
    return new Uri(uri + stringBuilder.ToString());
}

Usage

用法

Uri uri = new Uri("http://www.example.com/index.php").AttachParameters(new NameValueCollection
                                                                           {
                                                                               {"Bill", "Gates"},
                                                                               {"Steve", "Jobs"}
                                                                           });

Result

结果

http://www.example.com/index.php?Bill=Gates&Steve=Jobs

http://www.example.com/index.php?Bill=Gates&Steve=Jobs

回答by Todd Menier

You might want to check out Flurl[disclosure: I'm the author], a fluent URL builder with optional companion lib that extends it into a full-blown REST client.

您可能想查看Flurl[披露:我是作者],这是一个流畅的 URL 构建器,带有可选的配套库,可将其扩展为成熟的 REST 客户端。

var result = await "https://api.com"
    // basic URL building:
    .AppendPathSegment("endpoint")
    .SetQueryParams(new {
        api_key = ConfigurationManager.AppSettings["SomeApiKey"],
        max_results = 20,
        q = "Don't worry, I'll get encoded!"
    })
    .SetQueryParams(myDictionary)
    .SetQueryParam("q", "overwrite q!")

    // extensions provided by Flurl.Http:
    .WithOAuthBearerToken("token")
    .GetJsonAsync<TResult>();

Check out the docsfor more details. The full package is available on NuGet:

查看文档以获取更多详细信息。NuGet 上提供了完整的包:

PM> Install-Package Flurl.Http

PM> Install-Package Flurl.Http

or just the stand-alone URL builder:

或者只是独立的 URL 构建器:

PM> Install-Package Flurl

PM> Install-Package Flurl

回答by Sam Harwell

The RFC 6570 URI Template libraryI'm developing is capable of performing this operation. All encoding is handled for you in accordance with that RFC. At the time of this writing, a beta release is available and the only reason it's not considered a stable 1.0 release is the documentation doesn't fully meet my expectations (see issues #17, #18, #32, #43).

我正在开发的RFC 6570 URI 模板库能够执行此操作。所有编码都根据该 RFC 为您处理。在撰写本文时,有一个 beta 版本可用,它不被视为稳定的 1.0 版本的唯一原因是文档没有完全满足我的期望(请参阅问题#17#18#32#43)。

You could either build a query string alone:

您可以单独构建一个查询字符串:

UriTemplate template = new UriTemplate("{?params*}");
var parameters = new Dictionary<string, string>
  {
    { "param1", "value1" },
    { "param2", "value2" },
  };
Uri relativeUri = template.BindByName(parameters);

Or you could build a complete URI:

或者您可以构建一个完整的 URI:

UriTemplate template = new UriTemplate("path/to/item{?params*}");
var parameters = new Dictionary<string, string>
  {
    { "param1", "value1" },
    { "param2", "value2" },
  };
Uri baseAddress = new Uri("http://www.example.com");
Uri relativeUri = template.BindByName(baseAddress, parameters);

回答by Rostov

For those who do not want to include System.Webin projects that don't already use it, you can use FormUrlEncodedContentfrom System.Net.Httpand do something like the following:

对于那些不想包含System.Web在尚未使用它的项目中的人,您可以使用FormUrlEncodedContentfromSystem.Net.Http并执行以下操作:

keyvaluepair version

键值对版本

string query;
using(var content = new FormUrlEncodedContent(new KeyValuePair<string, string>[]{
    new KeyValuePair<string, string>("ham", "Glazed?"),
    new KeyValuePair<string, string>("x-men", "Wolverine + Logan"),
    new KeyValuePair<string, string>("Time", DateTime.UtcNow.ToString()),
})) {
    query = content.ReadAsStringAsync().Result;
}

dictionary version

字典版

string query;
using(var content = new FormUrlEncodedContent(new Dictionary<string, string>()
{
    { "ham", "Glaced?"},
    { "x-men", "Wolverine + Logan"},
    { "Time", DateTime.UtcNow.ToString() },
})) {
    query = content.ReadAsStringAsync().Result;
}

回答by Jaider

Since I have to reuse this few time, I came up with this class that simply help to abstract how the query string is composed.

由于我必须重复使用这几次,所以我想出了这个类,它只是帮助抽象查询字符串的组成方式。

public class UriBuilderExt
{
    private NameValueCollection collection;
    private UriBuilder builder;

    public UriBuilderExt(string uri)
    {
        builder = new UriBuilder(uri);
        collection = System.Web.HttpUtility.ParseQueryString(string.Empty);
    }

    public void AddParameter(string key, string value) {
        collection.Add(key, value);
    }

    public Uri Uri{
        get
        {
            builder.Query = collection.ToString();
            return builder.Uri;
        }
    }

}

The use will be simplify to something like this:

使用将简化为如下所示:

var builder = new UriBuilderExt("http://example.com/");
builder.AddParameter("foo", "bar<>&-baz");
builder.AddParameter("bar", "second");
var uri = builder.Uri;

that will return the uri: http://example.com/?foo=bar%3c%3e%26-baz&bar=second

这将返回 uri:http: //example.com/?foo= bar%3c%3e%26-baz&bar= second

回答by Waleed A.K.

Thanks to "Darin Dimitrov", This is the extension methods.

感谢“Darin Dimitrov”,这是扩展方法。

 public static partial class Ext
{
    public static Uri GetUriWithparameters(this Uri uri,Dictionary<string,string> queryParams = null,int port = -1)
    {
        var builder = new UriBuilder(uri);
        builder.Port = port;
        if(null != queryParams && 0 < queryParams.Count)
        {
            var query = HttpUtility.ParseQueryString(builder.Query);
            foreach(var item in queryParams)
            {
                query[item.Key] = item.Value;
            }
            builder.Query = query.ToString();
        }
        return builder.Uri;
    }

    public static string GetUriWithparameters(string uri,Dictionary<string,string> queryParams = null,int port = -1)
    {
        var builder = new UriBuilder(uri);
        builder.Port = port;
        if(null != queryParams && 0 < queryParams.Count)
        {
            var query = HttpUtility.ParseQueryString(builder.Query);
            foreach(var item in queryParams)
            {
                query[item.Key] = item.Value;
            }
            builder.Query = query.ToString();
        }
        return builder.Uri.ToString();
    }
}

回答by Diego Mendes

I couldn't find a better solution than creating a extension method to convert a Dictionary to QueryStringFormat. The solution proposed by Waleed A.K. is good as well.

我找不到比创建一个扩展方法来将字典转换为 QueryStringFormat 更好的解决方案。Waleed AK 提出的解决方案也很好。

Follow my solution:

按照我的解决方案:

Create the extension method:

创建扩展方法:

public static class DictionaryExt
{
    public static string ToQueryString<TKey, TValue>(this Dictionary<TKey, TValue> dictionary)
    {
        return ToQueryString<TKey, TValue>(dictionary, "?");
    }

    public static string ToQueryString<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, string startupDelimiter)
    {
        string result = string.Empty;
        foreach (var item in dictionary)
        {
            if (string.IsNullOrEmpty(result))
                result += startupDelimiter; // "?";
            else
                result += "&";

            result += string.Format("{0}={1}", item.Key, item.Value);
        }
        return result;
    }
}

And them:

还有他们:

var param = new Dictionary<string, string>
          {
            { "param1", "value1" },
            { "param2", "value2" },
          };
param.ToQueryString(); //By default will add (?) question mark at begining
//"?param1=value1&param2=value2"
param.ToQueryString("&"); //Will add (&)
//"&param1=value1&param2=value2"
param.ToQueryString(""); //Won't add anything
//"param1=value1&param2=value2"

回答by illegal-immigrant

TL;DR: do not use accepted version as It's completely broken in relation to handling unicode characters, and never use internal API

TL;DR:不要使用接受的版本,因为它在处理 unicode 字符方面完全被破坏,并且永远不要使用内部 API

I've actually found weird double encoding issue with the accepted solution:

我实际上在接受的解决方案中发现了奇怪的双重编码问题:

So, If you're dealing with characters which need to be encoded, accepted solution leads to double encoding:

因此,如果您正在处理需要编码的字符,那么公认的解决方案会导致双重编码:

  • query parameters are auto encoded by using NameValueCollectionindexer (and this uses UrlEncodeUnicode, not regular expected UrlEncode(!))
  • Then, when you call uriBuilder.Uriit creates new Uriusing constructor which does encoding one more time(normal url encoding)
  • That cannot be avoided by doing uriBuilder.ToString()(even though this returns correct Uriwhich IMO is at least inconsistency, maybe a bug, but that's another question) and then using HttpClientmethod accepting string - client still creates Uriout of your passed string like this: new Uri(uri, UriKind.RelativeOrAbsolute)
  • 查询参数是使用NameValueCollection索引器自动编码的(这使用UrlEncodeUnicode,而不是常规的预期UrlEncode(!)
  • 然后,当你调用uriBuilder.Uri它时,Uri使用构造函数创建新的,它会再编码一次(正常的 url 编码)
  • 这是无法避免的uriBuilder.ToString()(即使这返回正确的UriIMO 至少是不一致的,可能是一个错误,但这是另一个问题)然后使用HttpClient接受字符串的方法 -客户端仍然Uri像这样从您传递的字符串中创建:new Uri(uri, UriKind.RelativeOrAbsolute)

Small, but full repro:

小而完整的再现:

var builder = new UriBuilder
{
    Scheme = Uri.UriSchemeHttps,
    Port = -1,
    Host = "127.0.0.1",
    Path = "app"
};

NameValueCollection query = HttpUtility.ParseQueryString(builder.Query);

query["cyrillic"] = "кирилиця";

builder.Query = query.ToString();
Console.WriteLine(builder.Query); //query with cyrillic stuff UrlEncodedUnicode, and that's not what you want

var uri = builder.Uri; // creates new Uri using constructor which does encode and messes cyrillic parameter even more
Console.WriteLine(uri);

// this is still wrong:
var stringUri = builder.ToString(); // returns more 'correct' (still `UrlEncodedUnicode`, but at least once, not twice)
new HttpClient().GetStringAsync(stringUri); // this creates Uri object out of 'stringUri' so we still end up sending double encoded cyrillic text to server. Ouch!

Output:

输出:

?cyrillic=%u043a%u0438%u0440%u0438%u043b%u0438%u0446%u044f

https://127.0.0.1/app?cyrillic=%25u043a%25u0438%25u0440%25u0438%25u043b%25u0438%25u0446%25u044f

As you may see, no matter if you do uribuilder.ToString()+ httpClient.GetStringAsync(string)or uriBuilder.Uri+ httpClient.GetStringAsync(Uri)you end up sending double encoded parameter

如您所见,无论您是uribuilder.ToString()+httpClient.GetStringAsync(string)还是uriBuilder.Uri+,httpClient.GetStringAsync(Uri)您最终都会发送双重编码的参数

Fixed example could be:

固定示例可能是:

var uri = new Uri(builder.ToString(), dontEscape: true);
new HttpClient().GetStringAsync(uri);

But this uses obsoleteUriconstructor

但这使用过时的Uri构造函数

P.S on my latest .NET on Windows Server, Uriconstructor with bool doc comment says "obsolete, dontEscape is always false", but actually works as expected (skips escaping)

PS 在我最新的 Windows Server 上的 .NET 上,Uri带有 bool doc 注释的构造函数说“已过时,dontEscape 始终为 false”,但实际上按预期工作(跳过转义)

So It looks like another bug...

所以它看起来像另一个错误......

And even this is plain wrong - it send UrlEncodedUnicode to server, not just UrlEncoded what server expects

甚至这也是完全错误的 - 它向服务器发送 UrlEncodedUnicode,而不仅仅是服务器期望的 UrlEncoded

Update: one more thing is, NameValueCollection actually does UrlEncodeUnicode, which is not supposed to be used anymore and is incompatible with regular url.encode/decode (see NameValueCollection to URL Query?).

更新:还有一件事是,NameValueCollection 实际上做了 UrlEncodeUnicode,它不应该再被使用并且与常规 url.encode/decode 不兼容(请参阅NameValueCollection 到 URL 查询?)。

So the bottom line is: never use this hack with NameValueCollection query = HttpUtility.ParseQueryString(builder.Query);as it will mess your unicode query parameters. Just build query manually and assign it to UriBuilder.Querywhich will do necessary encoding and then get Uri using UriBuilder.Uri.

所以底线是:永远不要使用这个 hack ,NameValueCollection query = HttpUtility.ParseQueryString(builder.Query);因为它会弄乱你的 unicode 查询参数。只需手动构建查询并将其分配给UriBuilder.Query将进行必要的编码,然后使用UriBuilder.Uri.

Prime example of hurting yourself by using code which is not supposed to be used like this

通过使用不应该像这样使用的代码来伤害自己的主要例子