TL;DR:不要使用接受的版本,因为它在处理 unicode 字符方面完全被破坏了,并且永远不要使用内部 API
我实际上发现接受的解决方案存在奇怪的双重编码问题:
因此,如果您正在处理需要编码的字符,则可接受的解决方案会导致双重编码:
- 查询参数使用
NameValueCollection 索引器自动编码(这使用UrlEncodeUnicode,不是常规预期的UrlEncode(!))
- 然后,当您调用
uriBuilder.Uri 时,它会使用构造函数创建新的 Uri,会再编码一次(普通 url 编码)
-
通过
uriBuilder.ToString() 无法避免这种情况(即使这会返回正确的Uri,其中 IMO 至少是不一致的,可能是一个错误,但这是另一个问题)然后使用 HttpClient 方法接受字符串- 客户端仍然会从您传递的字符串中创建Uri,如下所示: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.Uri + httpClient.GetStringAsync(Uri),最终都会发送双重编码参数
固定示例可能是:
var uri = new Uri(builder.ToString(), dontEscape: true);
new HttpClient().GetStringAsync(uri);
但这使用 过时 Uri 构造函数
P.S 在我最新的 Windows Server .NET 上,Uri 带有 bool doc 注释的构造函数说“已过时,dontEscape 始终为假”,但实际上按预期工作(跳过转义)
所以它看起来像另一个错误......
即使这是完全错误的 - 它会将 UrlEncodedUnicode 发送到服务器,而不仅仅是服务器期望的 UrlEncoded
更新:还有一件事是,NameValueCollection 实际上是 UrlEncodeUnicode,它不应该再使用并且与常规 url.encode/decode 不兼容(请参阅NameValueCollection to URL Query?)。
所以底线是:永远不要在NameValueCollection query = HttpUtility.ParseQueryString(builder.Query);上使用这个hack,因为它会弄乱你的unicode查询参数。只需手动构建查询并将其分配给 UriBuilder.Query,它将进行必要的编码,然后使用 UriBuilder.Uri 获取 Uri。
使用不应该这样使用的代码伤害自己的主要例子