TL; DR: não use a versão aceita, pois está completamente corrompida em relação ao tratamento de caracteres unicode e nunca use a API interna
Na verdade, eu encontrei um problema estranho de codificação dupla com a solução aceita:
Portanto, se você estiver lidando com caracteres que precisam ser codificados, a solução aceita levará à codificação dupla:
- os parâmetros de consulta são codificados automaticamente usando o
NameValueCollection
indexador ( e isso usa UrlEncodeUnicode
, não o esperado regularmente UrlEncode
(!) )
- Então, quando você o chama
uriBuilder.Uri
, cria um novo Uri
construtor usando, que codifica mais uma vez (codificação de URL normal)
- Isso não pode ser evitado fazendo
uriBuilder.ToString()
(mesmo que isso retorne correto, Uri
qual IMO é pelo menos inconsistente, talvez um bug, mas essa é outra questão) e, em seguida, usando o HttpClient
método de aceitação de string - o cliente ainda cria Uri
sua string passada da seguinte maneira:new Uri(uri, UriKind.RelativeOrAbsolute)
Repro pequeno, mas completo:
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!
Resultado:
?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 você pode ver, não importa se você faz uribuilder.ToString()
+ httpClient.GetStringAsync(string)
ou uriBuilder.Uri
+httpClient.GetStringAsync(Uri)
você acaba enviando parâmetro codificado duplo
Exemplo fixo pode ser:
var uri = new Uri(builder.ToString(), dontEscape: true);
new HttpClient().GetStringAsync(uri);
Mas isso usa construtor obsoleto Uri
PS no meu .NET mais recente no Windows Server, Uri
construtor com comentário bool doc diz "obsoleto, o dontEscape é sempre falso", mas realmente funciona conforme o esperado (ignora a fuga)
Então parece outro bug ...
E mesmo isso está completamente errado - ele envia UrlEncodedUnicode para o servidor, não apenas UrlEncoded o que o servidor espera
Atualização: mais uma coisa é que NameValueCollection realmente faz UrlEncodeUnicode, que não deve mais ser usado e é incompatível com url.encode / decode regular (consulte NameValueCollection to URL Query? ).
Portanto, o ponto principal é: nunca use esse hack,NameValueCollection query = HttpUtility.ParseQueryString(builder.Query);
pois isso interferirá nos parâmetros de consulta unicode. Basta criar a consulta manualmente e atribuí-la à UriBuilder.Query
codificação necessária e, em seguida, obter o Uri UriBuilder.Uri
.
Exemplo excelente de se machucar usando código que não deve ser usado assim