如果发生异常,“Content-encoding”标头将从HttpHandler响应中消失

时间:2012-01-12 23:05:16

标签: c# asp.net http-headers httphandler http-compression

我有一个自定义的HttpHandler,我在其中手动启用输出压缩,如下所示:

context.Response.AppendHeader("Content-encoding", "gzip");
context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress);

这适用于大多数请求,但遇到异常时,“Content-encoding”标头会从响应中消失,而压缩过滤器仍保留在原位。结果是错误页面被gzip压缩,但浏览器没有收到表示该事实的标题。然后,浏览器尝试将仍然压缩的数据显示为文本,即gobbledygook

完整的测试用例代码如下所示。尝试交替禁用压缩或不抛出异常。

有人可以解释“内容编码”标题消失的原因吗?

我想我可以简单地启用压缩作为处理程序执行的 last 事务,因此如果遇到异常,它永远不会到达添加压缩过滤器的点;但我看到的行为让我感到害怕。任何人都可以确认吗?

public class TestHandler : IHttpHandler 
{
    public void ProcessRequest(HttpContext context)
    {
        CompressResponse(context);
        context.Response.Write("Hello world");

        // Throw an exception for testing purposes
        throw new Exception("Just testing...");
    }

    private void CompressResponse(HttpContext context)
    {
        string acceptEncoding = context.Request.Headers["Accept-Encoding"];
        if (String.IsNullOrEmpty(acceptEncoding))
        {
            return;
        }

        // gzip or wildcard
        if (acceptEncoding.ToLower().Contains("gzip") || acceptEncoding.Contains("*"))
        {
            context.Response.AppendHeader("Content-encoding", "gzip");
            context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress);
            return;
        }

        // Also handles deflate (not shown here)
        // <snip>
    }

    public bool IsReusable
    {
        get { return true; }
    }
}

编辑:我在测试用例中看到的仍然编码的响应的屏幕截图:http://i.imgur.com/49Vcl.png

4 个答案:

答案 0 :(得分:1)

WebForms 应用程序上强制使用gzip时,我遇到了同样的事情。为了解决这个问题,我必须清除Global.asax.cs中 Application_Error 方法中的过滤器

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

发生这种情况的原因是b / c在应用程序出错之前设置了过滤器。由于某种原因,黄屏错误消息清除了内容编码标头,但对响应过滤器没有任何作用。

答案 1 :(得分:0)

如果您有异常,那么服务器将刷新当前设置的标题和内容,因为它们是错误的,就像您在执行完所有操作后一样。

至少,很明显你要发送的200状态(因为所有不改变状态的成功响应发送200,并且在未处理的异常时它不再成功)是错误的,但是其他一切都与你要做的事情有关但却未能实现,所以这一切都是错的,一切都会好起来。

虽然过滤器没有重置。

如果合适,可以重置错误页面中的标题,或者除非您确定所有内容都已准备好进行刷新,否则不要设置过滤器。我会选择前者,没有理由不能压缩错误页面。

如果您拨打Flush(),则无法发送标头,因为您已经刷新了。标题将去哪里?

答案 2 :(得分:0)

我也遇到了这个问题。追踪是很复杂的。我不确定这整个情况的具体细节,但我认为发生的是内存泄漏。

当你第一次这样做时:

context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress);

您正在为Filter分配非托管资源。通常情况下,这将包含在using语句中,以便在出现任何错误的情况下正确处理

所以,当出现问题时,就会出现问题。 Filter包含一个仍然打开的流,即使响应是用黄色死屏写入的。接下来是疯狂(如你的屏幕截图所示)。

不要害怕!实际上有一种简单的方法可以解决这个问题。处理过滤器。幸运的是,已有一个地方可以应用这种全局检查过滤器处理。

<强>的global.asax.cs

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
        filters.Add(new HandleErrorAttribute());//default handler
        filters.Add(new HandleErrorEncodingAttribute());//extra check for filter disposal
}

错误处理程序名称空间

public class HandleErrorEncodingAttribute : FilterAttribute, IExceptionFilter
{
    public virtual void OnException(ExceptionContext filterContext)
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }
        if (filterContext.IsChildAction)
        {
            return;
        }
        // If custom errors are disabled, we need to let the normal ASP.NET exception handler
        // execute so that the user can see useful debugging information.
        if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
        {
            filterContext.HttpContext.Response.Filter.Dispose();//fixes response stream
            return;
        }
    }
}

答案 3 :(得分:-1)

我测试你的代码,我找不到任何问题。是的,没有设置gzip,但是过滤器也没有设置,asp得到控件并发送错误。

强制标题刷新会产生真正的问题

 CompressResponse(context);
 context.Response.Flush(); 

如果我强制使用gzip标头,则页面无法正确呈现。

两个人认为这可能是你的问题。您没有设置页面编码

context.Response.ContentEncoding = new UTF8Encoding();

并且您没有设置ContentType

context.Response.ContentType = "text/plain";

也许其中一些原因是您没有更正页面渲染。在我的测试中,即便如此,你所描述的问题也没有出现。