C# 压缩HTTP获取响应

C# 压缩HTTP获取响应,c#,asp.net-web-api,http-compression,C#,Asp.net Web Api,Http Compression,我目前正在将我的几个MVC3控制器迁移到MVC4API控制器。 我通过插入ActionFilterAttribute并重写OnActionExecutiong方法,实现了MVC3控制器Get方法响应的压缩机制。经过一些研究,我发现我需要使用System.Web.HttpFilters中的actionfilter方法。如果有人能分享一段示例代码,让我开始使用GZip压缩HTTP响应,那就太好了。如果您使用的是iis7+,我会说将压缩留给IIS,因为它支持GZip压缩。只是 另一方面,压缩距离控制器

我目前正在将我的几个MVC3控制器迁移到MVC4API控制器。
我通过插入
ActionFilterAttribute
并重写
OnActionExecutiong
方法,实现了MVC3控制器Get方法响应的压缩机制。经过一些研究,我发现我需要使用
System.Web.HttpFilters
中的
actionfilter方法。如果有人能分享一段示例代码,让我开始使用GZip压缩HTTP响应,那就太好了。如果您使用的是iis7+,我会说将压缩留给IIS,因为它支持GZip压缩。只是

另一方面,压缩距离控制器的金属太近。理想情况下,控制器应该在比字节和流更高的级别上工作。

最简单的方法是直接在IIS级别上工作

如果要在应用程序级别执行此操作,可以编写自定义委派消息处理程序,如所示:


使用类并编写以下代码

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CompressFilter : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext context)
    {
        var acceptedEncoding = context.Response.RequestMessage.Headers.AcceptEncoding.First().Value;
        if (!acceptedEncoding.Equals("gzip", StringComparison.InvariantCultureIgnoreCase)
        && !acceptedEncoding.Equals("deflate", StringComparison.InvariantCultureIgnoreCase))
        {
            return;
        }
        context.Response.Content = new CompressedContent(context.Response.Content, acceptedEncoding);
    }
}
现在创建另一个类并编写以下代码

public class CompressedContent : HttpContent
{
    private readonly string _encodingType;
    private readonly HttpContent _originalContent;
    public CompressedContent(HttpContent content, string encodingType = "gzip")
    {
        if (content == null)
        {
            throw new ArgumentNullException("content");
        }
        _originalContent = content;
        _encodingType = encodingType.ToLowerInvariant();
        foreach (var header in _originalContent.Headers)
        {
            Headers.TryAddWithoutValidation(header.Key, header.Value);
        }
        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;
        switch (_encodingType)
        {
            case "gzip":
                compressedStream = new GZipStream(stream, CompressionMode.Compress, true);
                break;
            case "deflate":
                compressedStream = new DeflateStream(stream, CompressionMode.Compress, true);
                break;
            default:
                compressedStream = stream;
                break;
        }
        return _originalContent.CopyToAsync(compressedStream).ContinueWith(tsk =>
        {
            if (compressedStream != null)
            {
                compressedStream.Dispose();
            }
        });
    }
}
现在在控制器或任何api操作方法中使用以下属性

[Route("GetData")]
[CompressFilter]         
public HttpResponseMessage GetData()
{
}
[路由(“获取数据”)]
[压缩过滤器]
公共HttpResponseMessage GetData()
{
}

我也遇到了同样的问题,尽管我已经启用了IIS压缩。在您的情况下,是IIS压缩,还是您创建了自定义处理程序?是的,我使用了自定义处理程序,就像Darin在这里提到的那样。我认为这段代码中有一个bug(以及在web上发现的类似示例中):内容长度标题设置不正确,因为内容长度标题是从gzip内容复制的。通过将StringContent传递给压缩处理程序,可以很容易地再现这一点。要解决此问题,需要将带有
originalContent.Headers
的行固定为这样:
originalContent.Headers.Where(x=>x.Key!=“Content Length”)
如果未提供接受编码,代码将失败
if(response.RequestMessage.Headers.AcceptEncoding!=null)
应该是
if(response.RequestMessage.Headers.AcceptEncoding.Any())
我建议在SendAsync中,在encodingType的分配和response.Content的分配之间添加以下内容,以允许错误响应在不压缩的情况下返回
if(response.StatusCode!=HttpStatusCode.OK | | | response.Content==null | | string.IsNullOrWhiteSpace(encodingType))返回响应;
我需要用以下代码替换AcceptEncoding检查:if(response.RequestMessage.Headers.AcceptEncoding.Any()){string encodingType=response.RequestMessage.Headers.AcceptEncoding.First().Value;if(response.Content!=null){response.Content=new CompressedContent(response.Content,encodingType);}如何合并response.Content.LoadIntoBufferAsync()以获取响应内容的长度(response.Content.Headers.ContentLength)然后如果小于某个阈值,则从压缩中排除结果?当在设置response.Content之前添加上述行时,调用以超时/死锁结束。我在Web API上配置了OWIN中间件,这是唯一对我有效的解决方案。此外,您可以真正针对您想要压缩的内容。好的解决方案!Th如果您在控制器方法中执行“return(Ok());”,is将失败,因为_originalContent将为null,并且您将得到一个关于“异步操作未返回System.Threading.Tasks.Task对象”的异常……我如何修复它?啊,只需在OnActionExecuted()中添加此项:“if(context.Response.Content==null)return”;它也将失败(崩溃)如果请求中没有Accept Encoding标头,则使用First()方法。改为:string acceptedEncoding=string.Empty;var acceptedEncodingHeaders=context.Response.RequestMessage.Headers.AcceptEncoding;if(acceptedEncodingHeaders.Any())acceptedEncoding=acceptedEncodingHeaders.First().Value;一般来说,我同意,但是IIS级别的压缩需要配置使用它的任何服务器。
public class CompressedContent : HttpContent
{
    private readonly string _encodingType;
    private readonly HttpContent _originalContent;
    public CompressedContent(HttpContent content, string encodingType = "gzip")
    {
        if (content == null)
        {
            throw new ArgumentNullException("content");
        }
        _originalContent = content;
        _encodingType = encodingType.ToLowerInvariant();
        foreach (var header in _originalContent.Headers)
        {
            Headers.TryAddWithoutValidation(header.Key, header.Value);
        }
        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;
        switch (_encodingType)
        {
            case "gzip":
                compressedStream = new GZipStream(stream, CompressionMode.Compress, true);
                break;
            case "deflate":
                compressedStream = new DeflateStream(stream, CompressionMode.Compress, true);
                break;
            default:
                compressedStream = stream;
                break;
        }
        return _originalContent.CopyToAsync(compressedStream).ContinueWith(tsk =>
        {
            if (compressedStream != null)
            {
                compressedStream.Dispose();
            }
        });
    }
}
[Route("GetData")]
[CompressFilter]         
public HttpResponseMessage GetData()
{
}