【问题标题】:Can gzip compression be selectively disabled in ASP.NET/IIS 7?可以在 ASP.NET/IIS 7 中选择性地禁用 gzip 压缩吗?
【发布时间】:2024-01-19 03:23:01
【问题描述】:

我正在使用长期异步 HTTP 连接通过 AJAX 向客户端发送进度更新。启用压缩后,更新不会以离散块的形式接收(原因很明显)。禁用压缩(通过向<system.webServier> 添加<urlCompression> 元素)确实解决了问题:

<urlCompression doStaticCompression="true" doDynamicCompression="false" />

但是,这会在站点范围内禁用压缩。我想为除此之外的所有其他控制器和/或操作保留压缩。这可能吗?还是我必须使用自己的 web.config 创建一个新站点/区域?欢迎提出任何建议。

附:写入 HTTP 响应的代码是:

var response = HttpContext.Response;
response.Write(s);
response.Flush();

【问题讨论】:

    标签: c# asp.net asp.net-mvc-3 iis-7 compression


    【解决方案1】:

    @Aristos 的回答将适用于 WebForms,但在他的帮助下,我调整了一个更符合 ASP.NET/MVC 方法的解决方案。

    创建一个新过滤器以提供 gzip 压缩功能:

    public class GzipFilter : ActionFilterAttribute
    {
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            base.OnActionExecuted(filterContext);
    
            var context = filterContext.HttpContext;
            if (filterContext.Exception == null && 
                context.Response.Filter != null &&
                !filterContext.ActionDescriptor.IsDefined(typeof(NoGzipAttribute), true))
            {
                string acceptEncoding = context.Request.Headers["Accept-Encoding"].ToLower();;
    
                if (acceptEncoding.Contains("gzip"))
                {
                    context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress);
                    context.Response.AppendHeader("Content-Encoding", "gzip");
                }                       
                else if (acceptEncoding.Contains("deflate"))
                {
                    context.Response.Filter = new DeflateStream(context.Response.Filter, CompressionMode.Compress);
                    context.Response.AppendHeader("Content-Encoding", "deflate");
                } 
            }
        }
    }
    

    创建NoGzip 属性:

    public class NoGzipAttribute : Attribute {
    }
    

    使用 web.config 防止 IIS7 压缩:

    <system.webServer>
        ...
        <urlCompression doStaticCompression="true" doDynamicCompression="false" />
    </system.webServer>
    

    在 Global.asax.cs 中注册您的全局过滤器:

    protected void Application_Start()
    {
        ...
        GlobalFilters.Filters.Add(new GzipFilter());
    }
    

    最后,消费NoGzip属性:

    public class MyController : AsyncController
    {
        [NoGzip]
        [NoAsyncTimeout]
        public void GetProgress(int id)
        {
            AsyncManager.OutstandingOperations.Increment();
            ...
        }
    
        public ActionResult GetProgressCompleted() 
        {
            ...
        }
    }
    

    附:再次感谢@Aristos,感谢他的有益想法和解决方案。

    【讨论】:

      【解决方案2】:

      我找到了一种更简单的方法来做到这一点。您可以选择性地禁用默认的 IIS 压缩(假设它在您的 web.config 中启用),而不是选择性地进行自己的压缩。

      只需删除请求上的接受编码编码标头,IIS 不会压缩页面。

      (global.asax.cs:)

      protected void Application_BeginRequest(object sender, EventArgs e)
      {
          try
          {
              HttpContext.Current.Request.Headers["Accept-Encoding"] = "";
          }
          catch(Exception){}
      }
      

      【讨论】:

      • server 端修改 request 标头是非常糟糕的做法。您基本上是在对服务器进行黑客攻击以伪造请求标头以修改服务器端行为。至少可以这么说,这令人担忧。
      【解决方案3】:

      你自己设置 gzip 压缩怎么样?在 Application_BeginRequest 检查您何时喜欢压缩以及何时不压缩。这是一个示例代码。

      protected void Application_BeginRequest(Object sender, EventArgs e)
      {
          string cTheFile = HttpContext.Current.Request.Path;
          string sExtentionOfThisFile = System.IO.Path.GetExtension(cTheFile);
      
          if (sExtentionOfThisFile.Equals(".aspx", StringComparison.InvariantCultureIgnoreCase))
          {
              string acceptEncoding = MyCurrentContent.Request.Headers["Accept-Encoding"].ToLower();;
      
              if (acceptEncoding.Contains("deflate") || acceptEncoding == "*")
              {
                  // defalte
                  HttpContext.Current.Response.Filter = new DeflateStream(prevUncompressedStream,
                      CompressionMode.Compress);
                  HttpContext.Current.Response.AppendHeader("Content-Encoding", "deflate");
              } else if (acceptEncoding.Contains("gzip"))
              {
                  // gzip
                  HttpContext.Current.Response.Filter = new GZipStream(prevUncompressedStream,
                      CompressionMode.Compress);
                  HttpContext.Current.Response.AppendHeader("Content-Encoding", "gzip");
              }       
          }
      }
      

      【讨论】:

      • 感谢您的帮助。我已根据您的指导创建了我在回答中描述的解决方案。