【问题标题】:"Content-encoding" header disappears from HttpHandler response if an exception occurs如果发生异常,“Content-encoding”标头将从 HttpHandler 响应中消失
【发布时间】:2012-02-09 05:57:23
【问题描述】:

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

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

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

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

谁能解释为什么“内容编码”标题消失了?

我想我可以简单地启用压缩作为处理程序所做的最后件事,这样如果遇到异常,它就永远不会到达添加压缩过滤器的位置;但我看到的行为让我觉得是个错误。谁能确认一下?

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

【问题讨论】:

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


    【解决方案1】:

    WebForms 应用程序上强制使用 gzip 时,我也遇到了同样的情况。为了修复它,我必须清除 Global.asax.cs 中 Application_Error 方法中的过滤器

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

    发生这种情况的原因是在应用程序出错之前设置了过滤器。并且由于某种原因,黄屏错误消息清除了 Content-encoding 标头,但对响应过滤器没有任何作用。

    【讨论】:

      【解决方案2】:

      如果您有异常,则服务器将刷新当前设置的标头和内容,因为它们是错误的,因为您毕竟有异常。

      至少,很明显,您要发送的 200 状态(因为所有不更改状态的成功响应都会发送 200,并且在出现未处理的异常时不再成功)是错误的,但是其他一切都与您将要做但未能实现的事情相关,所以一切都错了,一切都会过去。

      过滤器不会重置。

      在适当的情况下重置错误页面中的标题,或者不要设置过滤器,除非您可以确定所有内容都已准备好刷新。我会选择前者,没有理由也不能压缩错误页面。

      如果您调用了Flush(),则无法发送标头,因为好吧,因为您已刷新。标题要去哪里?

      【讨论】:

      • 因此,当发生错误时,处理程序会删除所有已写入(但尚未刷新)的标题和内容,但忽略删除我已放置的过滤器?
      • 是的。这很烦人,但我认为过滤器在概念上是为了在管道中处于更高的层次,所以它对某人来说是有意义的......
      • 在我看来,如果错误处理程序删除了我编写的标头,那么它也应该删除我分配的过滤器。我想知道这是一个错误,还是设计使然...否则,您能否澄清“您要发送的 200 状态错误”的意思?异常处理程序是设置 500 状态的原因。为什么要手动设置200以外的状态?
      • 我不相信保留过滤器是正确的决定。 “您要发送的 200 个”是指如果没有发生异常,您将发送的 200 个。
      • 如果没有发生异常,那不是说200是正确的吗?
      【解决方案3】:

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

      当你第一次这样做时:

      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;
              }
          }
      }
      

      【讨论】:

        【解决方案4】:

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

        强制刷新标题是一个真正的问题

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

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

        Two 认为这里可能是您的问题。您没有设置页面编码

        context.Response.ContentEncoding = new UTF8Encoding();
        

        而你没有设置 ContentType

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

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

        【讨论】:

        • 所以如果你按原样执行我的代码,你会得到一个适当的黄屏死机,而不是仍然压缩的 gobbledygook?
        • 是的,照原样,我进行复制/粘贴,我得到了适当的黄屏死机。当我刷新()时出现问题!!!!!!!!!你会在错误发生前冲洗某个地方吗? gzip 没有出现,但过滤器也没有设置。
        • 不,我没有在我的测试用例的任何地方手动刷新。您能否验证您的浏览器是否正在发送“Accept-Encoding:gzip”标头?
        • @DavidMills 是的,当然我已经验证过了,我在有和没有例外的情况下进行了测试。我使用谷歌浏览器的最后一个版本。没有例外,我看到了 gzip,除了我没有看到 gzip。
        • 嗯。您使用的是哪个版本的 .NET 框架?我碰巧正在使用 Visual Studio 2008 和 .NET 3.5 进行测试。也许 .NET 4 发生了一些变化?
        猜你喜欢
        • 2020-12-11
        • 2020-02-11
        • 2022-01-09
        • 1970-01-01
        • 2020-07-15
        • 2016-11-05
        • 1970-01-01
        • 2015-12-13
        • 1970-01-01
        相关资源
        最近更新 更多