TL; DR: не використовуйте прийняту версію, оскільки вона повністю зламана стосовно обробки символів Unicode, і ніколи не використовуйте внутрішній API
Я фактично знайшов дивну проблему подвійного кодування з прийнятим рішенням:
Отже, якщо ви маєте справу з символами, які потрібно закодувати, прийняте рішення призводить до подвійного кодування:
- Параметри запиту кодуються автоматично за допомогою
NameValueCollectionіндексатора ( і це використовує UrlEncodeUnicode, не регулярно очікуване UrlEncode(!) )
- Потім, коли ви телефонуєте,
uriBuilder.Uriвін створює новий Uriза допомогою конструктора, який виконує кодування ще раз (звичайне кодування URL)
- Цього не можна уникнути,
uriBuilder.ToString() незважаючи на те, що якщо ви повертаєте правильно, UriIMO має принаймні непослідовність, можливо, помилку, але це вже інше питання), а потім використовуючи 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 конструктор
PS в моєму останньому .NET на Windows Server, Uriконструктор з коментарем bool doc говорить: "застарілий, dontEscape завжди помилковий", але насправді працює як очікувалося (пропускає втечу)
Так виглядає ще одна помилка ...
І навіть це явно неправильно - він надсилає UrlEncodedUnicode на сервер, а не просто UrlEncoded, що сервер очікує
Оновлення: ще одне - NameValueCollection насправді робить UrlEncodeUnicode, який більше не повинен використовуватися і несумісний із звичайним url.encode / decode (див. NameValueCollection до URL-запиту? ).
Отже, підсумок: ніколи не використовуйте цей злом,NameValueCollection query = HttpUtility.ParseQueryString(builder.Query); оскільки він зіпсує ваші параметри запиту unicode. Просто створіть запит вручну та призначте його, UriBuilder.Queryякий зробить необхідне кодування, а потім отримайте Uri з використанням UriBuilder.Uri.
Прекрасний приклад пошкодження себе, використовуючи код, який не повинен використовуватися таким чином