HttpClient:在运行时有条件地设置AcceptEncoding压缩

时间:2015-02-26 23:16:32

标签: c# gzip .net-4.5 dotnet-httpclient winrt-async

我们正在尝试在我们使用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();

这创建了正确的请求,但gzipped响应在返回时未解压缩,导致响应乱码。我发现在构建HttpClientHandler时必须包含HttpClient

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

这一切都运行良好,但我们想要更改客户端是否在运行时发送Accept-Encoding: gzip标头 ,并且似乎没有任何方式可以访问或更改传递给HttpClientHandler构造函数后的HttpClient。此外,如果HttpRequestMessage定义了HttpClientHandler对象的标头,则它们对HttpClient对象的标头没有任何影响。

如果没有在每次更改时重新创建HttpClientHandler,有没有办法做到这一点?

编辑:我还尝试修改对AutomaticDecompression的引用以在运行时更改{{1}},但这会引发此异常:

  

此实例已启动一个或多个请求。只能在发送第一个请求之前修改属性。

3 个答案:

答案 0 :(得分:9)

你几乎就是第一个例子,你只需要自己放气。 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();
}

答案 1 :(得分:3)

如果您想使用相同的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();
}

答案 2 :(得分:1)

根据上面的评论,重新创建HttpClient实际上是唯一(强大的)方法。可以实现手动解压缩,但是可靠/有效地确定内容是否已被编码似乎非常困难,以确定是否应用解码。