TL; DR: no use la versión aceptada ya que está completamente rota en relación con el manejo de caracteres Unicode, y nunca use API interna
De hecho, he encontrado un extraño problema de doble codificación con la solución aceptada:
Entonces, si se trata de caracteres que necesitan ser codificados, la solución aceptada conduce a una doble codificación:
- los parámetros de consulta se codifican automáticamente mediante el uso de
NameValueCollection
indexador ( y esto utiliza UrlEncodeUnicode
, no se espera regularmente UrlEncode
(!) )
- Luego, cuando lo llama
uriBuilder.Uri
, crea un nuevo Uri
constructor usando que codifica una vez más (codificación de URL normal)
- Eso no se puede evitar haciendo
uriBuilder.ToString()
(a pesar de que esto devuelve correcto Uri
qué IMO es al menos inconsistencia, tal vez un error, pero esa es otra pregunta) y luego usando el HttpClient
método de aceptación de cadena: el cliente aún crea a Uri
partir de su cadena pasada de esta manera:new Uri(uri, UriKind.RelativeOrAbsolute)
Pequeño pero completo repro:
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!
Salida:
?cyrillic=%u043a%u0438%u0440%u0438%u043b%u0438%u0446%u044f
https://127.0.0.1/app?cyrillic=%25u043a%25u0438%25u0440%25u0438%25u043b%25u0438%25u0446%25u044f
Como puede ver, no importa si hace uribuilder.ToString()
+ httpClient.GetStringAsync(string)
o uriBuilder.Uri
+ httpClient.GetStringAsync(Uri)
, termina enviando un parámetro codificado doble
Ejemplo fijo podría ser:
var uri = new Uri(builder.ToString(), dontEscape: true);
new HttpClient().GetStringAsync(uri);
Pero esto usa un constructor obsoleto Uri
PS en mi último .NET en Windows Server, el Uri
constructor con el comentario de bool doc dice "obsoleto, dontEscape siempre es falso", pero en realidad funciona como se espera (se escapa)
Entonces parece otro error ...
E incluso esto es simplemente incorrecto: envía UrlEncodedUnicode al servidor, no solo UrlEncoded lo que el servidor espera
Actualización: una cosa más es que NameValueCollection realmente hace UrlEncodeUnicode, que se supone que ya no se debe usar y es incompatible con url.encode / decode (¿Ver NameValueCollection to URL Query? ).
Entonces, la conclusión es: nunca use este truco,NameValueCollection query = HttpUtility.ParseQueryString(builder.Query);
ya que alterará sus parámetros de consulta Unicode. Simplemente construya la consulta manualmente y asígnele lo UriBuilder.Query
que hará la codificación necesaria y luego use Uri UriBuilder.Uri
.
Primer ejemplo de lastimarse a sí mismo usando un código que no se debe usar de esta manera