作为一项实验,我一直在想着创建一个IIS托管模块来动态修改CSS文件。背景故事是所有的Web应用程序保留了我们附加到每个JS,CSS和图像引用(以HTML格式)的共享和通用版本号,我想修改CSS的实际内容以将版本号附加到图像引用在CSS中。所以,例如:
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压缩版本,并且在任何子序列请求中,我的自定义流传递了gzip流。我还没有找到一种方法来检测它,但可能存在一个更深层次的问题,我真的不太了解IIS模块应该如何工作。我的模块应该对另一个模块做出任何假设似乎是错误的,或者至少应该能够定义它们在IIS配置中处理请求的顺序。但是,这似乎没有意义,因为每个模块都可以注册以处理请求生命周期中的任何事件。
因此,如果我不得不将我的想法重新表述为更精确的问题,那么问题将是:
如何在StaticFileModule模块读取文件和服务器之后但在StaticCompressionModule之前确保我的后处理/模块被调用?问题甚至有意义吗?根据上述观察,似乎有点矛盾。
答案 0 :(得分:2)
静态文件压缩的配置包括特定的文件或mime类型,我忘了哪一个。如果要使用静态压缩,则只需要在实际上是静态的文件上。您将使用HttpModule重写它们的事实意味着它们不再是静态的。因此,您可以对这些文件类型使用动态压缩,正如您所料,您需要确保动态压缩包含适当的mime类型或文件扩展名。