返回 HttpResponseMessage 时的 WebAPI Gzip
Posted
技术标签:
【中文标题】返回 HttpResponseMessage 时的 WebAPI Gzip【英文标题】:WebAPI Gzip when returning HttpResponseMessage 【发布时间】:2014-09-09 01:01:48 【问题描述】:我有一个返回 HttpResponseMessage
的 WebAPI 控制器,我想添加 gzip 压缩。这是服务器代码:
using System.Net.Http;
using System.Web.Http;
using System.Web;
using System.IO.Compression;
[Route("SomeRoute")]
public HttpResponseMessage Post([FromBody] string value)
HttpContext context = HttpContext.Current;
context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress);
HttpContext.Current.Response.AppendHeader("Content-encoding", "gzip");
HttpContext.Current.Response.Cache.VaryByHeaders["Accept-encoding"] = true;
return new SomeClass().SomeRequest(value);
这是 ajax 调用的客户端代码,使用 jquery:
$.ajax(
url: "/SomeRoute",
type: "POST",
cache: "false",
data: SomeData,
beforeSend: function (jqXHR) jqXHR.setRequestHeader('Accept-Encoding', 'gzip'); ,
success: function(msg) ...
当我运行这个时,服务器代码返回没有错误,但客户端错误:
(failed)
net::ERR_CONTENT_DECODING_FAILED
当我用 Fiddler 看时,我看到的是这样的:
我需要进行哪些更改才能使 Web 服务返回客户端正常处理的 gzip 压缩内容?我知道我也可以使用 HttpModule 或通过 IIS 上的某些设置来执行此操作,但是这两个选项都不适合托管的场景:
请注意,我不是在寻找 IIS 设置,因为我无权访问该设置(托管)。
【问题讨论】:
你看过这个吗? ***.com/a/10446108/263003 看看weblog.west-wind.com/posts/2012/Apr/28/… @JeowLiHuan:我希望我可以用更少的步骤来完成。 检查这个 - ***.com/a/3653766/2164198 并尝试使用建议的包装器以避免不良行为。另请参阅此答案***.com/a/7629079/2164198 从外观上看,您可以直接在 IIS 上执行此操作。链接有点旧---***.com/questions/702124/enable-iis7-gzip 【参考方案1】:添加这些 NuGet 包:
Microsoft.AspNet.WebApi.Extensions.Compression.Server System.Net.Http.Extensions.Compression.Client
然后在App_Start\WebApiConfig.cs
中添加一行代码:
GlobalConfiguration.Configuration.MessageHandlers.Insert(0, new ServerCompressionHandler(new GZipCompressor(), new DeflateCompressor()));
这样就行了!
详情在:
NuGet package page GitHub希望对您有所帮助。
**在@JCisar 发表评论后更新
ASP.Net Core 更新
Nuget 包是
Microsoft.AspNetCore.ResponseCompression
【讨论】:
这比开启动态 IIS 压缩好多了 谢谢!虽然,包装确实说它已经过时(我猜是 2016 年 1 月 28 日)。任何人都知道这是否被我应该使用的东西所取代?我没有看到任何提及它。 @JCisar 根据包作者的说法,你应该使用由他维护的those new packages。 请注意,我无法通过此方法使其工作,实际上必须在Register
方法中使用 config.MessageHandlers.Insert(0, new ServerCompressionHandler(new GZipCompressor(), new DeflateCompressor()));
这是一个过时的包,它只安装 Microsoft.AspNet.WebApi.Extensions.Compression.Server 和 System.Net.Http.Extensions.Compression.Client 包。【参考方案2】:
如果您有权访问 IIS 配置
您不能只应用标头并希望它被压缩 - 响应不会被压缩。
您需要删除您添加的标头,并确保在您的 IIS 服务器上启用了动态压缩和静态内容压缩。
其中一位评论者在 stakoverflow 上提到了一个很好的资源链接,该链接显示了如何做到这一点:
Enable IIS7 gzip
请注意,如果已经安装了动态压缩(不在 IIS 的默认安装中),它只会在 web.config 中设置值
您可以在 MSDN 文档中找到相关信息:http://www.iis.net/configreference/system.webserver/httpcompression
简单压缩
下面是使用一个简单的例子来做你自己的压缩这个例子是使用 Visual Studio 项目模板中的 Web Api MVC 4 项目。要使压缩对 HttpResponseMessages 起作用,您必须实现自定义 MessageHandler。请参阅下面的工作示例。
参见下面的代码实现。
请注意,我试图保持方法与您的示例相同。
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
namespace MvcApplication1.Controllers
public class ValuesController : ApiController
public class Person
public string name get; set;
// GET api/values
public IEnumerable<string> Get()
HttpContext.Current.Response.Cache.VaryByHeaders["accept-encoding"] = true;
return new [] "value1", "value2" ;
// GET api/values/5
public HttpResponseMessage Get(int id)
HttpContext.Current.Response.Cache.VaryByHeaders["accept-encoding"] = true;
var TheHTTPResponse = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
TheHTTPResponse.Content = new StringContent("\"asdasdasdsadsad\": 123123123 ", Encoding.UTF8, "text/json");
return TheHTTPResponse;
public class EncodingDelegateHandler : DelegatingHandler
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
return base.SendAsync(request, cancellationToken).ContinueWith<HttpResponseMessage>((responseToCompleteTask) =>
HttpResponseMessage response = responseToCompleteTask.Result;
if (response.RequestMessage.Headers.AcceptEncoding != null &&
response.RequestMessage.Headers.AcceptEncoding.Count > 0)
string encodingType = response.RequestMessage.Headers.AcceptEncoding.First().Value;
response.Content = new CompressedContent(response.Content, encodingType);
return response;
,
TaskContinuationOptions.OnlyOnRanToCompletion);
public class CompressedContent : HttpContent
private HttpContent originalContent;
private string encodingType;
public CompressedContent(HttpContent content, string encodingType)
if (content == null)
throw new ArgumentNullException("content");
if (encodingType == null)
throw new ArgumentNullException("encodingType");
originalContent = content;
this.encodingType = encodingType.ToLowerInvariant();
if (this.encodingType != "gzip" && this.encodingType != "deflate")
throw new InvalidOperationException(string.Format("Encoding '0' is not supported. Only supports gzip or deflate encoding.", this.encodingType));
// copy the headers from the original content
foreach (KeyValuePair<string, IEnumerable<string>> header in originalContent.Headers)
this.Headers.TryAddWithoutValidation(header.Key, header.Value);
this.Headers.ContentEncoding.Add(encodingType);
protected override bool TryComputeLength(out long length)
length = -1;
return false;
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
Stream compressedStream = null;
if (encodingType == "gzip")
compressedStream = new GZipStream(stream, CompressionMode.Compress, leaveOpen: true);
else if (encodingType == "deflate")
compressedStream = new DeflateStream(stream, CompressionMode.Compress, leaveOpen: true);
return originalContent.CopyToAsync(compressedStream).ContinueWith(tsk =>
if (compressedStream != null)
compressedStream.Dispose();
);
还将新的消息处理程序添加到应用的配置中。
using System.Web.Http;
using MvcApplication1.Controllers;
namespace MvcApplication1
public static class WebApiConfig
public static void Register(HttpConfiguration config)
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/controller/id",
defaults: new id = RouteParameter.Optional
);
config.MessageHandlers.Add(new ValuesController.EncodingDelegateHandler());
config.EnableSystemDiagnosticsTracing();
自定义处理程序由 - Kiran Challa (http://blogs.msdn.com/b/kiranchalla/archive/2012/09/04/handling-compression-accept-encoding-sample.aspx) 编写
还有更好的示例可以实现入站流的放气,您可以在下面查看示例:
http://www.codeproject.com/Articles/557232/Implementing-a-Custom-DelegatingHandler-in-ASP-NET http://ronaldrosiernet.azurewebsites.net/blog/2013/07/16/implement_compression_in_aspnet_web_api另外,我在 github 上发现了一个非常好的项目,它支持所有这些。
https://github.com/azzlack/Microsoft.AspNet.WebApi.MessageHandlers.Compression请注意,当我自己得出此答案时,您的 cmets 中的 Simon 从此答案之日起 2 天前建议了这种方法。
【讨论】:
我不是在寻找 IIS 解决方案,因为我无权访问它。这就是为什么我需要从 WebAPI 中进行压缩。 使用benfoster.io/blog/aspnet-web-api-compression 上的示例,该示例在 iis 之外执行。 这在托管环境中不起作用:无需编辑任何 IIS 设置或安装任何 Nuget 包的解决方案是向您的 WEB API 添加 MessageHandler。
这将捕获带有“AcceptEncoding”标头的请求,并使用 System.IO.Compression 库中的 Build 对其进行压缩。
public class CompressHandler : DelegatingHandler
private static CompressHandler _handler;
private CompressHandler()
public static CompressHandler GetSingleton()
if (_handler == null)
_handler = new CompressHandler();
return _handler;
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
return base.SendAsync(request, cancellationToken).ContinueWith<HttpResponseMessage>((responseToCompleteTask) =>
HttpResponseMessage response = responseToCompleteTask.Result;
var acceptedEncoding =GetAcceptedEncoding(response);
if(acceptedEncoding!=null)
response.Content = new CompressedContent(response.Content, acceptedEncoding);
return response;
,
TaskContinuationOptions.OnlyOnRanToCompletion);
private string GetAcceptedEncoding(HttpResponseMessage response)
string encodingType=null;
if (response.RequestMessage.Headers.AcceptEncoding != null && response.RequestMessage.Headers.AcceptEncoding.Any())
encodingType = response.RequestMessage.Headers.AcceptEncoding.First().Value;
return encodingType;
public class CompressedContent : HttpContent
private HttpContent originalContent;
private string encodingType;
public CompressedContent(HttpContent content, string encodingType)
if (content == null)
throw new ArgumentNullException("content");
if (encodingType == null)
throw new ArgumentNullException("encodingType");
originalContent = content;
this.encodingType = encodingType.ToLowerInvariant();
if (this.encodingType != "gzip" && this.encodingType != "deflate")
throw new InvalidOperationException(string.Format("Encoding '0' is not supported. Only supports gzip or deflate encoding.", this.encodingType));
// copy the headers from the original content
foreach (KeyValuePair<string, IEnumerable<string>> header in originalContent.Headers)
this.Headers.TryAddWithoutValidation(header.Key, header.Value);
this.Headers.ContentEncoding.Add(encodingType);
protected override bool TryComputeLength(out long length)
length = -1;
return false;
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
Stream compressedStream = null;
if (encodingType == "gzip")
compressedStream = new GZipStream(stream, CompressionMode.Compress, leaveOpen: true);
else if (encodingType == "deflate")
compressedStream = new DeflateStream(stream, CompressionMode.Compress, leaveOpen: true);
return originalContent.CopyToAsync(compressedStream).ContinueWith(tsk =>
if (compressedStream != null)
compressedStream.Dispose();
);
并将这个处理程序添加到您的 Global.asax.cs
GlobalConfiguration.Configuration.MessageHandlers.Insert(0, CompressHandler.GetSingleton());
向本·福斯特致敬。 ASP.NET Web API Compression
【讨论】:
谢谢!正是我需要替换一些过时的 NuGet 包。 :)【参考方案4】:只是通过 applicationHost.config
文件在 IIS 中启用压缩的附录。
Use the IIS config manager 进行更改或notepad.exe
编辑文件。我使用的是Notepad++
,尽管文件正在保存,但实际上并没有。
与 32/64 位环境、配置和编辑它们的程序有关。毁了我的下午!!
【讨论】:
以上是关于返回 HttpResponseMessage 时的 WebAPI Gzip的主要内容,如果未能解决你的问题,请参考以下文章
WEB API HttpResponseMessage 返回 JSONP
HttpResponseMessage.Content.ReasAsString 返回一个空字符串
ASP.NET MVC 网站(不是项目)是不是可以返回 HttpResponseMessage
在 .NET 核心中返回 HttpResponseMessage 时出错