TL; DR: non utilizzare la versione accettata poiché è completamente rotto in relazione alla gestione dei caratteri unicode e non utilizzare mai l'API interna
In realtà ho riscontrato uno strano problema di doppia codifica con la soluzione accettata:
Quindi, se hai a che fare con caratteri che devono essere codificati, la soluzione accettata porta alla doppia codifica:
- i parametri della query vengono codificati automaticamente utilizzando l'
NameValueCollection
indicizzatore ( e questo utilizza UrlEncodeUnicode
, non previsto normalmente UrlEncode
(!) )
- Quindi, quando lo chiami
uriBuilder.Uri
, crea un nuovo Uri
usando il costruttore che codifica ancora una volta (codifica URL normale)
- Questo non può essere evitato facendo
uriBuilder.ToString()
(anche se questo restituisce correttamente Uri
quale IMO è almeno incoerenza, forse un bug, ma questa è un'altra domanda) e quindi usando il HttpClient
metodo che accetta la stringa - il client crea ancora Uri
dalla tua stringa passata in questo modo:new Uri(uri, UriKind.RelativeOrAbsolute)
Riproduzione piccola ma completa:
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!
Produzione:
?cyrillic=%u043a%u0438%u0440%u0438%u043b%u0438%u0446%u044f
https://127.0.0.1/app?cyrillic=%25u043a%25u0438%25u0440%25u0438%25u043b%25u0438%25u0446%25u044f
Come puoi vedere, non importa se fai uribuilder.ToString()
+ httpClient.GetStringAsync(string)
o uriBuilder.Uri
+ httpClient.GetStringAsync(Uri)
finisci per inviare un doppio parametro codificato
L'esempio fisso potrebbe essere:
var uri = new Uri(builder.ToString(), dontEscape: true);
new HttpClient().GetStringAsync(uri);
Ma questo utilizza un costruttore obsoleto Uri
PS sul mio ultimo .NET su Windows Server, il Uri
costruttore con il commento bool doc dice "obsoleto, dontEscape è sempre falso", ma in realtà funziona come previsto (salta la fuga)
Quindi sembra un altro bug ...
E anche questo è assolutamente sbagliato: invia UrlEncodedUnicode al server, non solo UrlEncoded ciò che il server si aspetta
Aggiornamento: un'altra cosa è, NameValueCollection in realtà fa UrlEncodeUnicode, che non dovrebbe più essere usato ed è incompatibile con il normale url.encode / decode (vedi NameValueCollection alla query URL? ).
Quindi la linea di fondo è: non usare mai questo hack conNameValueCollection query = HttpUtility.ParseQueryString(builder.Query);
in quanto rovinerà i parametri della query unicode. Basta compilare manualmente la query e assegnarla alla UriBuilder.Query
quale farà la codifica necessaria e quindi ottenere Uri utilizzando UriBuilder.Uri
.
Primo esempio di farsi del male usando il codice che non dovrebbe essere usato in questo modo