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
PS บน. NET ล่าสุดของฉันบน Windows Server คอนUri
สตรัคเตอร์ที่มีความคิดเห็น bool doc บอกว่า "ล้าสมัย dontEscape เป็นเท็จเสมอ" แต่ใช้งานได้จริงตามที่คาดไว้ (ข้ามการหลบหนี)
ดังนั้นดูเหมือนว่าข้อผิดพลาดอื่น ...
และแม้กระทั่งสิ่งนี้ผิดธรรมดา - มันส่ง UrlEncodedUnicode ไปยังเซิร์ฟเวอร์ไม่ใช่แค่ UrlEncoded สิ่งที่เซิร์ฟเวอร์คาดหวัง
อัปเดต: อีกสิ่งหนึ่งคือ NameValueCollection จริง ๆ แล้ว UrlEncodeUnicode ซึ่งไม่ควรใช้อีกต่อไปและเข้ากันไม่ได้กับ url.encode / ถอดรหัสปกติ (ดูที่NameValueCollection กับ URL Query )
ดังนั้นบรรทัดล่างคือ: อย่าใช้แฮ็คนี้NameValueCollection query = HttpUtility.ParseQueryString(builder.Query);
เพราะมันจะทำให้พารามิเตอร์การสืบค้นยูนิโค้ดของคุณยุ่งเหยิง เพียงแค่สร้างแบบสอบถามด้วยตนเองและกำหนดให้UriBuilder.Query
ซึ่งจะทำการเข้ารหัสที่จำเป็นและจากนั้นได้รับ Uri UriBuilder.Uri
ใช้
ตัวอย่างที่เด่นชัดของการทำร้ายตัวเองโดยใช้รหัสซึ่งไม่ควรใช้เช่นนี้