HttpClient:在运行时有条件地设置 AcceptEncoding 压缩
HttpClient: Conditionally set AcceptEncoding compression at runtime
我们正在尝试在使用 HttpClient
的客户端中实施 user-determined(在设置屏幕上)可选的 gzip 压缩,因此我们可以记录和比较多个不同调用的性能一段的时间。我们的第一次尝试是简单地有条件地添加 header,如下所示:
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 响应未在 return 上解压缩,导致响应出现乱码。我发现在构造 HttpClient
:
时我们必须包含 HttpClientHandler
HttpClient _client = new HttpClient(new HttpClientHandler
{
AutomaticDecompression = DecompressionMethods.GZip
});
一切正常,但我们想更改客户端是否在运行时发送 Accept-Encoding: gzip
header ,但没有在 HttpClientHandler
传递给 HttpClient
构造函数后,似乎可以通过任何方式访问或更改它。此外,更改 HttpRequestMessage
object 的 header 对请求的 header 没有任何影响,如果它们是由 HttpClientHandler
.
有什么方法可以做到这一点而无需每次更改时都重新创建 HttpClient
?
编辑:我还尝试修改对 HttpClientHandler
的引用以在运行时更改 AutomaticDecompression
,但这会引发此异常:
This instance has already started one or more requests. Properties can only be modified before sending the first request.
你几乎完成了第一个示例,你只需要自己缩小流。 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();
}
根据上面的评论,重新创建 HttpClient 确实是执行此操作的唯一(可靠)方法。手动解压可以实现,但是好像很难reliably/efficiently判断内容是否已经编码,判断是否应用解码。
如果你想使用同一个HttpClient,并且只想对某些请求启用压缩,则无法使用自动解压。启用自动解压后,框架也会重置响应的Content-Encoding
header。这意味着您无法确定响应是否真的被压缩了。顺便说一句,如果你打开自动解压,响应的Content-Length
header也匹配解压内容的大小。
所以你需要手动解压内容。以下示例显示了 gzip-compressed 内容的实现(如 @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();
}
我们正在尝试在使用 HttpClient
的客户端中实施 user-determined(在设置屏幕上)可选的 gzip 压缩,因此我们可以记录和比较多个不同调用的性能一段的时间。我们的第一次尝试是简单地有条件地添加 header,如下所示:
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 响应未在 return 上解压缩,导致响应出现乱码。我发现在构造 HttpClient
:
HttpClientHandler
HttpClient _client = new HttpClient(new HttpClientHandler
{
AutomaticDecompression = DecompressionMethods.GZip
});
一切正常,但我们想更改客户端是否在运行时发送 Accept-Encoding: gzip
header ,但没有在 HttpClientHandler
传递给 HttpClient
构造函数后,似乎可以通过任何方式访问或更改它。此外,更改 HttpRequestMessage
object 的 header 对请求的 header 没有任何影响,如果它们是由 HttpClientHandler
.
有什么方法可以做到这一点而无需每次更改时都重新创建 HttpClient
?
编辑:我还尝试修改对 HttpClientHandler
的引用以在运行时更改 AutomaticDecompression
,但这会引发此异常:
This instance has already started one or more requests. Properties can only be modified before sending the first request.
你几乎完成了第一个示例,你只需要自己缩小流。 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();
}
根据上面的评论,重新创建 HttpClient 确实是执行此操作的唯一(可靠)方法。手动解压可以实现,但是好像很难reliably/efficiently判断内容是否已经编码,判断是否应用解码。
如果你想使用同一个HttpClient,并且只想对某些请求启用压缩,则无法使用自动解压。启用自动解压后,框架也会重置响应的Content-Encoding
header。这意味着您无法确定响应是否真的被压缩了。顺便说一句,如果你打开自动解压,响应的Content-Length
header也匹配解压内容的大小。
所以你需要手动解压内容。以下示例显示了 gzip-compressed 内容的实现(如 @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();
}