web-dev-qa-db-ja.com

HttpClient:実行時にAcceptEncoding圧縮を条件付きで設定します

HttpClientを使用するクライアントにユーザーが決定した(設定画面で)オプションのgzip圧縮を実装しようとしています。これにより、一定期間にわたるさまざまな呼び出しのパフォーマンスをログに記録して比較できます。最初の試みは、次のように単純に条件付きでヘッダーを追加することでした。

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();

これにより正しいリクエストが作成されましたが、gzip圧縮された応答は返送時に解凍されず、応答が文字化けしていました。 HttpClientHandlerを作成するときに、HttpClientを含める必要があることがわかりました。

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

これはすべてうまく機能しますが、クライアントがAccept-Encoding: gzipヘッダーを送信するかどうかを変更したいと思います実行時そしてHttpClientHandlerにアクセスまたは変更する方法がないようですHttpClientコンストラクターに渡されます。さらに、HttpRequestMessageオブジェクトのヘッダーを変更しても、それらがHttpClientHandlerによって定義されている場合、リクエストのヘッダーには影響しません。

これが変更されるたびにHttpClientを再作成せずにこれを行う方法はありますか?

編集:実行時にHttpClientHandlerを変更するために、AutomaticDecompressionへの参照も変更しようとしましたが、この例外がスローされます。

このインスタンスはすでに1つ以上のリクエストを開始しています。プロパティは、最初のリクエストを送信する前にのみ変更できます。

10
pcdev

上記のコメントによると、HttpClientを再作成することが、これを行う唯一の(堅牢な)方法です。手動で解凍することはできますが、コンテンツがエンコードされているかどうかを確実に/効率的に判断し、デコードを適用するかどうかを判断することは非常に難しいようです。

1
pcdev

最初の例でほぼ完了です。ストリームを自分で収縮させる必要があります。 MSの GZipSteam はこれを助けます:

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();
}
10
Todd Menier

同じHttpClientを使用し、一部のリクエストに対してのみ圧縮を有効にしたい場合は、自動解凍を使用できません。自動解凍が有効になっている場合、フレームワークは応答のContent-Encodingヘッダーもリセットします。これは、応答が本当に圧縮されているかどうかを確認できないことを意味します。ちなみに、自動解凍をオンにすると、応答のContent-Lengthヘッダーも解凍されたコンテンツのサイズと一致します。

したがって、コンテンツを手動で解凍する必要があります。次のサンプルは、gzipで圧縮されたコンテンツの実装を示しています(@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();
}
3
Markus