抛出异常时gzip / deflate失败

时间:2012-02-14 18:59:59

标签: asp.net-mvc asp.net-mvc-3 gzip

我在ASP.NET MVC 3中使用gzip / deflate ActionFilterAttribute时遇到了一个有趣的问题。如果我的应用程序抛出异常,而不是获得YSOD,我会得到一整页乱码,如下所示。

  

I�%&/m�{J�J��t�� $ @ IG#)* EVE F的@흼{{; N” ?\fdlJɞ〜|!?“安永”)= y6hZ2kjuU ?+ _ X-:   TWV&LT; [〜2G2ʋyhYՋ吨   _N M l { , Xn Q } *g 7 ~ �j'u>K�{_��IW4�>�U�w�|=-fYzR-�������|��<&�o�Z()*�S!U��k�g�������j��.����b}��ή�9X/��J�Iն��Q���z�i�n�-g٤���ݞ��Y^����H�8/��k�}]7�ǜ@�{|�g��wUd�O����죫y���o-�����ݏ��� �ZHv,�d]��١�>o3�=�3x�7MN�����������Ow���w�.o��φ�<؟M����;���vg���A>��䋟{YޟN�����Φ�$p>q����/�!�y��9�2��two������?������Ӈ���n�9�r�^����!������{���ag�?\1*c�?!�bي?xIuf   ?{'P $ V&安培;!=#sl_0΃wss廌⼽[R [KP \ 7M(O4ߛ&GT;&GT; @“|| vy5QꆦRJSK&安培;ߛ PV&LT; Ct1hOIy {j]的我   D'P&LT; $ ,�'M��r{-�}��CF�؛�����A��9��[�½�� �! 2�� �:��!��{�t�;�߇'y��M��+�M^#x^\����Q��jM�l��?(�]� ��IZ�ݟ[����+4#"�:�X����m�������dv>������iL�̀I |�fL�TU��ho�� �{L��_t��5�o?���h�O�UY]#�u�[���G�ޞ�=���;��8���~����d�8k�w�����yw�ֺNXA [XMO F /噩;!Y〜

如果我删除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());

即使抛出异常,如何确保响应可读?

3 个答案:

答案 0 :(得分:8)

这是一个稍微好一点的答案,灵感来自于iaimtomisbe的回答。它允许您将所有代码保留在一个类中。

将以下覆盖添加到CompressAttribute类:

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

答案 1 :(得分:6)

这是因为当您的应用程序出现错误时,ASP.Net将删除所有自定义标题,但过滤器仍然存在。您可以根据应用程序错误重置过滤器,以避免问题消失。

protected void Application_Error(object sender, EventArgs e)
{
        Response.Filter = null;
}

答案 2 :(得分: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;
}

请随意批评这一点。