当抛出异常时gzip/deflate失败

6

我在ASP.NET MVC 3中使用gzip/deflate ActionFilterAttribute时遇到了一个有趣的问题。如果应用程序出现异常,我没有看到YSOD,而是看到了如下的一大堆乱码。

Sorry, the text you provided seems to be a garbled code that cannot be translated. Please provide a valid text to be translated.
如果我删除我的CompressAttribute,它将按预期工作(我会看到YSOD)。因此,似乎我的异常处理(来自Elmah.Contrib.Mvc的ElmahHandleErrorAttribute)停止了其余的过滤器,包括CompressAttribute,并且响应未被压缩。

相关代码:

public sealed class CompressAttribute : ActionFilterAttribute
{
    private const string _acceptEncodingHeader = "Accept-Encoding";
    private const string _contentEncodingHeader = "Content-Encoding";

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        HttpRequestBase request = filterContext.HttpContext.Request;

        string acceptEncoding = request.Headers[_acceptEncodingHeader];

        if (String.IsNullOrEmpty(acceptEncoding))
        {
            return;
        }

        acceptEncoding = acceptEncoding.ToUpperInvariant();

        HttpResponseBase response = filterContext.HttpContext.Response;

        if (acceptEncoding.Contains("GZIP"))
        {
            response.AppendHeader(_contentEncodingHeader, "gzip");
            response.Filter = new GZipStream(response.Filter, CompressionMode.Compress);
        }
        else if (acceptEncoding.Contains("DEFLATE"))
        {
            response.AppendHeader(_contentEncodingHeader, "deflate");
            response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress);
        }
    }
}

过滤器注册:

GlobalFilterCollection filters = GlobalFilters.Filters;
filters.Add(new ElmahHandleErrorAttribute(), 999); // Elmah.Contrib.Mvc
filters.Add(new CompressAttribute());

如何确保响应在抛出异常时仍然可读?

我也愿意尝试其他更好的方法来启用HTTP压缩。 - jrummell
你有研究过IIS内置的动态压缩吗? - amit_g
@amit_g 我没有。你有链接吗? - jrummell
1
这个和这个,还有更多的内容。问问谷歌吧 :) - amit_g
我喜欢IIS压缩的想法,因为一个设置可以适用于服务器上的所有站点。我会阅读一些资料。 - jrummell
3个回答

9

以下是受iaimtomisbehave回答启发的稍微更好的答案,它允许您将所有代码保留在一个类中。

向CompressAttribute类添加以下覆盖:

public override void OnResultExecuted(ResultExecutedContext filterContext)
{
    if (filterContext.Exception != null)
    {
        filterContext.HttpContext.Response.Filter = null;
    }
}

这实际上是我最终所做的。 - jrummell
2
如果我将它放在OnActionExecuted中,它就能工作,但在OnResultExecuted中不行。 - andrewpm

7
这是因为当您的应用程序出现错误时,ASP.Net会删除所有自定义标头,但过滤器仍然存在。您可以在应用程序错误时重置过滤器,这样问题就会消失。
protected void Application_Error(object sender, EventArgs e)
{
        Response.Filter = null;
}

0
在互联网上搜索了同样的问题并最终来到这里。非常有帮助的答案启发了我实现下面提到的自己的版本:
static public void EnableGzip()
{
    var c = HttpContext.Current;
    string a = c.Request.Headers["Accept-Encoding"];
    if (String.IsNullOrEmpty(a))
        return;
    if (!a.Contains("gzip"))
        return;
    c.Response.Filter = new GZipStream(
        c.Response.Filter, CompressionMode.Compress);
    c.Response.AppendHeader("Content-Encoding", "gzip");
    EventHandler errorHandler = null;
    errorHandler = delegate
    {
        c.Response.Filter = null;
        c.ApplicationInstance.Error -= errorHandler;
    };
    c.ApplicationInstance.Error += errorHandler;
}

请随意批评这个。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接