TL; DR: jangan gunakan versi yang diterima karena itu benar-benar rusak dalam kaitannya dengan penanganan karakter unicode, dan tidak pernah menggunakan API internal
Saya benar-benar menemukan masalah penyandian ganda yang aneh dengan solusi yang diterima:
Jadi, Jika Anda berurusan dengan karakter yang perlu disandikan, solusi yang diterima mengarah ke penyandian ganda:
- parameter kueri dikodekan secara otomatis dengan menggunakan
NameValueCollection
pengindeks ( dan ini menggunakan UrlEncodeUnicode
, tidak diharapkan biasa UrlEncode
(!) )
- Kemudian, ketika Anda memanggilnya
uriBuilder.Uri
membuat Uri
konstruktor menggunakan baru yang melakukan pengkodean sekali lagi (pengodean url normal)
- Itu tidak dapat dihindari dengan melakukan
uriBuilder.ToString()
(meskipun ini mengembalikan benar Uri
IMO mana yang setidaknya tidak konsisten, mungkin bug, tapi itu pertanyaan lain) dan kemudian menggunakan HttpClient
metode menerima string - klien masih membuat Uri
keluar dari string yang Anda kirimkan seperti ini:new Uri(uri, UriKind.RelativeOrAbsolute)
Repro kecil, tetapi penuh:
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!
Keluaran:
?cyrillic=%u043a%u0438%u0440%u0438%u043b%u0438%u0446%u044f
https://127.0.0.1/app?cyrillic=%25u043a%25u0438%25u0440%25u0438%25u043b%25u0438%25u0446%25u044f
Seperti yang Anda lihat, tidak masalah jika Anda melakukan uribuilder.ToString()
+ httpClient.GetStringAsync(string)
atau uriBuilder.Uri
+ httpClient.GetStringAsync(Uri)
Anda akhirnya mengirim parameter ganda yang dikodekan
Contoh tetap dapat:
var uri = new Uri(builder.ToString(), dontEscape: true);
new HttpClient().GetStringAsync(uri);
Tapi ini menggunakan konstruktor usang Uri
PS pada .NET terbaru saya di Windows Server, Uri
konstruktor dengan komentar bool doc mengatakan "usang, dontEscape selalu salah", tetapi sebenarnya berfungsi seperti yang diharapkan (melompat keluar)
Jadi sepertinya ada bug lain ...
Dan bahkan ini jelas salah - ia mengirim UrlEncodedUnicode ke server, bukan hanya UrlEncoded yang diharapkan server
Pembaruan: satu hal lagi adalah, NameValueCollection sebenarnya melakukan UrlEncodeUnicode, yang tidak seharusnya digunakan lagi dan tidak kompatibel dengan url.encode / decode biasa (lihat NameValueCollection to URL Query? ).
Jadi intinya adalah: jangan pernah menggunakan hack iniNameValueCollection query = HttpUtility.ParseQueryString(builder.Query);
karena akan mengacaukan parameter permintaan unicode Anda. Cukup buat query secara manual dan tetapkan ke UriBuilder.Query
yang akan melakukan pengkodean yang diperlukan dan kemudian gunakan Uri UriBuilder.Uri
.
Contoh utama menyakiti diri sendiri dengan menggunakan kode yang tidak seharusnya digunakan seperti ini