2015-02-26 25 views
5

Stiamo provando a implementare la compressione gzip facoltativa determinata dall'utente (su una schermata delle impostazioni) nel nostro client che utilizza HttpClient, in modo che possiamo registrare e confrontare le prestazioni su un numero di chiamate diverse su un periodo di tempo. Il nostro primo tentativo è stato quello di aggiungere semplicemente condizionato l'intestazione come segue:HttpClient: Compressa AcceptEncoding impostata in modo condizionale durante il runtime

HttpRequestMessage request = new HttpRequestMessage(Method, Uri); 
if (AcceptGzipEncoding) 
{ 
    _client.DefaultRequestHeaders.AcceptEncoding.Add(new System.Net.Http.Headers.StringWithQualityHeaderValue("gzip")); 
} 

//Send to the server 
result = await _client.SendAsync(request); 

//Read the content of the result response from the server 
content = await result.Content.ReadAsStringAsync(); 

Questo ha creato la richiesta corretta, ma la risposta gzip non è stato decompresso il ritorno, con una conseguente risposta confusa. Ho scoperto che abbiamo dovuto includere la HttpClientHandler quando si costruisce la HttpClient:

HttpClient _client = new HttpClient(new HttpClientHandler 
    { 
     AutomaticDecompression = DecompressionMethods.GZip 
    }); 

Tutto questo funziona bene, ma ci piacerebbe cambiare se il client invia l'intestazione Accept-Encoding: gzipin fase di esecuzione, e non ci fa sembra essere un modo per accedere o modificare il HttpClientHandler dopo che è passato al costruttore HttpClient. Inoltre, la modifica delle intestazioni dell'oggetto HttpRequestMessage non ha alcun effetto sulle intestazioni della richiesta se sono definite dallo HttpClientHandler.

C'è un modo per farlo senza ricreare il HttpClient ogni volta che questo cambia?

Edit: Ho provato anche di modificare un riferimento al HttpClientHandler di cambiare AutomaticDecompression in fase di esecuzione, ma che sta lanciando questa eccezione:

Questa istanza è già iniziata una o più richieste. Le proprietà possono essere modificate solo prima di inviare la prima richiesta.

+0

Solo per curiosità, ora che avete visto come a portata di mano decompressione automatica è, perché * non * ricreare il client ogni tempo che l'impostazione cambia? Questo è esattamente quello che farei qui, a meno che non ci sia un buon motivo per non farlo. –

+0

@ToddMenier Questa è una domanda molto valida. È qualcosa che stiamo considerando, ma credo che richieda un po 'di ristrutturazione, perché lo stesso HttpClient è legato a una serie di aree. Si tratta di un'applicazione di dimensioni contenute, ereditata dagli sviluppatori precedenti, quindi dobbiamo gestire con attenzione le modifiche. Lo vedremo questa settimana.Grazie per i commenti e l'aiuto. – pcdev

risposta

1

Come per le osservazioni di cui sopra, ricreando la HttpClient è davvero l'unico (robusta) modo per fare questo. La decompressione manuale può essere ottenuta ma sembra molto difficile determinare in modo affidabile/efficiente se il contenuto è stato codificato o meno, per determinare se applicare la decodifica.

7

Sei quasi arrivato con il primo esempio, hai solo bisogno di sgonfiare il flusso tu stesso. MS di GZipSteam aiuterà con questo:

HttpRequestMessage request = new HttpRequestMessage(Method, Uri); 
if (AcceptGzipEncoding) 
{ 
    _client.DefaultRequestHeaders.AcceptEncoding.Add(new System.Net.Http.Headers.StringWithQualityHeaderValue("gzip")); 
} 

//Send to the server 
result = await _client.SendAsync(request); 

//Read the content of the result response from the server 
using (Stream stream = await result.Content.ReadAsStreamAsync()) 
using (Stream decompressed = new GZipStream(stream, CompressionMode.Decompress)) 
using (StreamReader reader = new StreamReader(decompressed)) 
{ 
    content = reader.ReadToEnd(); 
} 
+1

Hmm, risulta che questa risposta funziona a meno che il server non decida di (o non sia configurato) di gzip la risposta, quindi cade. Sfortunatamente non c'è un modo semplice per ottenere l'intestazione "Content-Encoding" dall'oggetto HttpResponseMessage [vedi questo post] (https://social.msdn.microsoft.com/Forums/windowsapps/en-US/cb7417b5-ca3e-44f6 -a272-9e2f8fc5d9b8/portable-httpclient-hides-contentlength-and-contentencoding-headers-with-gzip-encoding? forum = wpdevelop), quindi sembra che ricreare HttpClient sia davvero l'unico modo per farlo. Grazie ancora per il tuo contributo. – pcdev

2

Se si desidera utilizzare lo stesso HttpClient e si desidera abilitare solo la compressione per alcune richieste, non è possibile utilizzare la decompressione automatica. Quando la decompressione automatica è abilitata, il framework reimposta anche l'intestazione Content-Encoding della risposta. Ciò significa che non sei in grado di scoprire se la risposta è stata realmente compressa o meno. A proposito, anche l'intestazione della risposta Content-Length corrisponde alla dimensione del contenuto decompresso se si attiva la decompressione automatica.

Quindi è necessario decomprimere il contenuto manualmente. L'esempio seguente mostra un'implementazione per i contenuti gzip-compresso (come dimostrato anche in @ di ToddMenier response):

private async Task<string> ReadContentAsString(HttpResponseMessage response) 
{ 
    // Check whether response is compressed 
    if (response.Content.Headers.ContentEncoding.Any(x => x == "gzip")) 
    { 
     // Decompress manually 
     using (var s = await response.Content.ReadAsStreamAsync()) 
     { 
      using (var decompressed = new GZipStream(s, CompressionMode.Decompress)) 
      { 
       using (var rdr As New IO.StreamReader(decompressed)) 
       { 
        return await rdr.ReadToEndAsync(); 
       } 
      } 
     } 
    else 
     // Use standard implementation if not compressed 
     return await response.Content.ReadAsStringAsync(); 
}