如果我想使用System.Net.HttpClient提交一个http获取请求,似乎没有api来添加参数,这是正确的吗?
是否有任何简单的api可用来构建查询字符串,不涉及构建名称值集合和url编码,然后最后连接它们? 我希望使用类似RestSharp的api(即AddParameter(..))
如果我想使用System.Net.HttpClient提交一个http获取请求,似乎没有api来添加参数,这是正确的吗?
是否有任何简单的api可用来构建查询字符串,不涉及构建名称值集合和url编码,然后最后连接它们? 我希望使用类似RestSharp的api(即AddParameter(..))
当前回答
TL;DR:不要使用公认的版本,因为它在处理unicode字符方面完全被破坏了,并且永远不要使用内部API
实际上,我在公认的解决方案中发现了奇怪的双编码问题:
所以,如果你处理的字符需要编码,接受的解决方案导致双重编码:
query parameters are auto encoded by using NameValueCollection indexer (and this uses UrlEncodeUnicode, not regular expected UrlEncode(!)) Then, when you call uriBuilder.Uri it creates new Uri using constructor which does encoding one more time (normal url encoding) That cannot be avoided by doing uriBuilder.ToString() (even though this returns correct Uri which IMO is at least inconsistency, maybe a bug, but that's another question) and then using HttpClient method accepting string - client still creates Uri out of your passed string like this: new Uri(uri, UriKind.RelativeOrAbsolute)
小,但完整的复制品:
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!
输出:
?cyrillic=%u043a%u0438%u0440%u0438%u043b%u0438%u0446%u044f
https://127.0.0.1/app?cyrillic=%25u043a%25u0438%25u0440%25u0438%25u043b%25u0438%25u0446%25u044f
正如你所看到的,不管你是使用uriBuilder. tostring () + httpClient.GetStringAsync(string)还是uriBuilder. tostring()。Uri + httpClient.GetStringAsync(Uri)你最终会发送双编码参数
固定的例子可以是:
var uri = new Uri(builder.ToString(), dontEscape: true);
new HttpClient().GetStringAsync(uri);
但这使用了过时的Uri构造函数
p.s.在我最新的。net在Windows服务器上,Uri构造函数与bool doc注释说“过时,dontEscape总是假的”,但实际上工作如预期(跳过转义)
所以它看起来像另一个bug…
甚至这是完全错误的-它发送UrlEncodedUnicode到服务器,而不仅仅是UrlEncoded服务器期望
更新:还有一件事是,NameValueCollection实际上做UrlEncodeUnicode,这是不应该再使用的,与常规url不兼容。encode/decode(参见NameValueCollection to URL Query?)
所以底线是:永远不要使用这个黑客NameValueCollection query = HttpUtility.ParseQueryString(builder.Query);因为它会扰乱你的unicode查询参数。只需手动构建查询并将其分配给UriBuilder。查询,它将进行必要的编码,然后使用UriBuilder.Uri获取Uri。
使用不应该这样使用的代码而伤害自己的最好例子
其他回答
接受答案的好部分,修改为使用UriBuilder.Uri.ParseQueryString()而不是HttpUtility.ParseQueryString():
var builder = new UriBuilder("http://example.com");
var query = builder.Uri.ParseQueryString();
query["foo"] = "bar<>&-baz";
query["bar"] = "bazinga";
builder.Query = query.ToString();
string url = builder.ToString();
我的答案与公认的答案/其他答案在全球范围内并无不同。我只是尝试为Uri类型创建一个扩展方法,它接受可变数量的参数。
public static class UriExtensions
{
public static Uri AddParameter(this Uri url, params (string Name, string Value)[] @params)
{
if (!@params.Any())
{
return url;
}
UriBuilder uriBuilder = new(url);
NameValueCollection query = HttpUtility.ParseQueryString(uriBuilder.Query);
foreach (var param in @params)
{
query[param.Name] = param.Value.Trim();
}
uriBuilder.Query = query.ToString();
return uriBuilder.Uri;
}
}
使用的例子:
var uri = new Uri("http://someuri.com")
.AddParameter(
("p1.name", "p1.value"),
("p2.name", "p2.value"),
("p3.name", "p3.value"));
由于我必须重用这几次,所以我提出了这个类,它只是帮助抽象查询字符串是如何组成的。
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;
}
}
}
使用将被简化成这样:
var builder = new UriBuilderExt("http://example.com/");
builder.AddParameter("foo", "bar<>&-baz");
builder.AddParameter("bar", "second");
var uri = builder.Uri;
将返回uri: http://example.com/?foo=bar%3c%3e%26-baz&bar=second
与罗斯托夫的帖子一样,如果你不想包含对系统的引用。在你的项目中,你可以使用System.Net.Http.Formatting中的FormDataCollection,然后做如下的事情:
使用System.Net.Http.Formatting.FormDataCollection
var parameters = new Dictionary<string, string>()
{
{ "ham", "Glaced?" },
{ "x-men", "Wolverine + Logan" },
{ "Time", DateTime.UtcNow.ToString() },
};
var query = new FormDataCollection(parameters).ReadAsNameValueCollection().ToString();
感谢“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();
}
}