TL; DR: không sử dụng phiên bản được chấp nhận vì nó hoàn toàn bị hỏng liên quan đến việc xử lý các ký tự unicode và không bao giờ sử dụng API nội bộ
Tôi thực sự đã tìm thấy vấn đề mã hóa kép kỳ lạ với giải pháp được chấp nhận:
Vì vậy, nếu bạn đang xử lý các ký tự cần được mã hóa, giải pháp được chấp nhận sẽ dẫn đến mã hóa kép:
- tham số truy vấn được tự động mã hóa bằng cách sử dụng bộ
NameValueCollection
chỉ mục ( và điều này sử dụng UrlEncodeUnicode
, không được mong đợi thường xuyên UrlEncode
(!) )
- Sau đó, khi bạn gọi
uriBuilder.Uri
nó sẽ tạo mới Uri
bằng cách sử dụng hàm tạo , mã hóa thêm một lần nữa (mã hóa url thông thường)
- Điều đó không thể tránh được bằng cách thực hiện
uriBuilder.ToString()
(mặc dù điều này trả về chính xác Uri
IMO ít nhất là không nhất quán, có thể là một lỗi, nhưng đó là một câu hỏi khác) và sau đó sử dụng HttpClient
chuỗi chấp nhận phương thức - client vẫn tạo Uri
ra chuỗi đã truyền của bạn như thế này:new Uri(uri, UriKind.RelativeOrAbsolute)
Nhỏ, nhưng repro đầy đủ:
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!
Đầu ra:
?cyrillic=%u043a%u0438%u0440%u0438%u043b%u0438%u0446%u044f
https://127.0.0.1/app?cyrillic=%25u043a%25u0438%25u0440%25u0438%25u043b%25u0438%25u0446%25u044f
Như bạn có thể thấy, bất kể bạn làm uribuilder.ToString()
+ httpClient.GetStringAsync(string)
hay uriBuilder.Uri
+ httpClient.GetStringAsync(Uri)
cuối cùng bạn sẽ gửi tham số được mã hóa kép
Ví dụ cố định có thể là:
var uri = new Uri(builder.ToString(), dontEscape: true);
new HttpClient().GetStringAsync(uri);
Nhưng điều này sử dụng lỗi thời Uri
constructor
PS trên .NET mới nhất của tôi trên Windows Server, nhà Uri
xây dựng với nhận xét bool doc nói "lỗi thời, dontEscape luôn sai", nhưng thực sự hoạt động như mong đợi (bỏ qua thoát)
Vì vậy, nó trông giống như một lỗi khác ...
Và thậm chí điều này hoàn toàn sai - nó gửi UrlEncodingUnicode đến máy chủ, không chỉ UrlEncoding những gì máy chủ mong đợi
Cập nhật: một điều nữa là, NameValueCollection thực sự có UrlEncodeUnicode, không được sử dụng nữa và không tương thích với url.encode / giải mã thông thường (xem NameValueCollection để truy vấn URL? ).
Vì vậy, điểm mấu chốt là: không bao giờ sử dụng bản hack nàyNameValueCollection query = HttpUtility.ParseQueryString(builder.Query);
vì nó sẽ làm rối các tham số truy vấn unicode của bạn. Chỉ cần xây dựng truy vấn theo cách thủ công và gán nó vào UriBuilder.Query
đó sẽ thực hiện mã hóa cần thiết và sau đó nhận Uri bằng cách sử dụng UriBuilder.Uri
.
Ví dụ điển hình về việc tự làm tổn thương chính mình bằng cách sử dụng mã không được sử dụng như thế này