自定义IIS模块与gzip冲突

问题描述:

作为一个实验,我一直在尝试创建一个IIS托管模块来动态修改CSS文件。背后的故事是,所有的Web应用程序都保留一个共享和通用的版本号,我们附加到每个JS,CSS和图像引用(HTML)中,并且我想修改CSS的实际内容以将版本号附加到图像引用在CSS中。所以,举例来说:自定义IIS模块与gzip冲突

span.warning { background-image: url(warning-icon.png) } 

应该变成:

span.warning { background-image: url(warning-icon.png?123) } 

现在,我知道有很多不同的方法解决这个问题(和一些也许更好),但如果任何人都可以我想知道回答我关于我一直在玩的那个问题。

到目前为止,我已经了解到托管的HTTP模块不能直接修改响应流(我认为),并且正确的方法是追加输出过滤器来完成工作。我已经写了下面的测试模块:

public class HttpCSSModule : IHttpModule 
{ 

    public void Init(HttpApplication httpApplication) 
    { 
     httpApplication.BeginRequest += new EventHandler(
      (s, e) => AttachFilter((HttpApplication)s)); 
    } 

    private void AttachFilter(HttpApplication httpApplication) 
    { 
     HttpRequest httpRequest = httpApplication.Context.Request; 
     HttpResponse httpResponse = httpApplication.Context.Response; 
     if (httpRequest.Path.EndsWith(".css", StringComparison.CurrentCultureIgnoreCase)) 
     { 
      if (!string.IsNullOrEmpty(httpRequest.Url.Query)) 
      { 
       httpResponse.Filter = new CSSResponseStreamFilter(
        httpResponse.Filter, httpRequest.Url.Query); 
      } 
     } 
    } 

    public void Dispose() 
    { 
    } 

    private class CSSResponseStreamFilter : Stream 
    { 

     private Stream inner; 
     private string version; 
     private MemoryStream responseBuffer = new MemoryStream(); 

     public CSSResponseStreamFilter(Stream inner, string version) 
     { 
      this.inner = inner; 
      this.version = version; 
     } 

     public override void Close() 
     { 

      if (responseBuffer.Length != 0) 
      { 

       string stylesheet = Encoding.ASCII.GetString(
        responseBuffer.GetBuffer(), 0, (int)responseBuffer.Length); 

       // crude, just testing 
       string versionedStylesheet = stylesheet. 
        Replace(".png", ".png" + version). 
        Replace(".jpg", ".jpg" + version). 
        Replace(".gif", ".gif" + version); 

       byte[] outputBytes = Encoding.ASCII.GetBytes(versionedStylesheet); 
       innerStream.Write(outputBytes, 0, outputBytes.Length); 

      } 

      innerStream.Close(); 

     } 

     public override void Write(byte[] buffer, int offset, int count) 
     { 
      responseBuffer.Write(buffer, offset, count); 
     }    

     // other Stream members 

    } 

} 

模块的工作原理,但并不总是和有件事情我不明白。最大的问题是当静态文件压缩打开时模块不工作。当打开静态文件压缩时,对CSS文件的第一个请求就像往常一样提供文件,但大概IIS会保留gzip版本,并且在任何子序列请求中,我的自定义流都会传递gzipped流。我还没有找到检测它的方法,但是可能存在更深层次的问题,因为我实际上可能不了解IIS模块应该如何工作。我的模块应该对另一个模块进行任何假设似乎是错误的,或者至少应该能够定义它们在IIS配置中处理请求的顺序。但是,这似乎没有道理,因为每个模块都可以注册以处理请求生命周期中的任何事件。

所以,如果我不得不重新制定我的想法,以更精致的问题,这些问题将是:

我如何确保我的后处理/模块称为文件由StaticFileModule模块读取和服务器之后,但在StaticCompressionModule之前?这个问题甚至有意义吗?根据以上观察,似乎有点矛盾。

+0

我有同样的问题,我正在工作几个小时来解决它。 :(你可以向我解释一下你是如何解决这个问题的吗? – iboware 2012-11-12 11:59:13

有配置为静态文件压缩包括特定的文件或MIME类型,我忘了哪一个。如果你想使用静态压缩,它只需要在实际上是静态的文件上。你将用HttpModule重写它们的事实意味着它们不再是静态的。因此,您可以对这些文件类型使用动态压缩,并且正如您所期望的,您需要确保您的动态压缩将包含适当的MIME类型或文件扩展名。

+0

你是对的,它解决了这个问题,它实际上更符合逻辑(它不再是一个静态文件),这也表明动态压缩模块(compdyn。 dll)和静态压缩模块(compstat.dll)的区别在于它们处理的类型列表不同,它们必须以不同的方式集成到IIS管道中。 – 2011-04-10 04:42:09