DC.Web.HttpCompress 压缩模块源码分析(二)
不知道大家思考的怎么样了,有没有什么思路,有的话可以一起讨论下。
现在说下这个压缩模块的大致思路。
1.首先,在客户端第一次请求页面时将html中<head>或<body>标签内的css和js引用替换成自定义格式,例如
<script src="js/js1/jquery-1.7.1.min.js" type="text/javascript"></script>
//替换为
<script src="/js/js1/js.axd?files=jquery-1.7.1.min.js" type="text/javascript"></script>
<link href="css/main.css" rel="stylesheet" type="text/css" />
//替换为
<link href="/css/css.axd?files=main.css" type="text/css" rel="stylesheet">
复制代码
即替换为"/Path/js.axd?files=文件1,文件2"。文件1和文件2位于同一路径下。css替换同理。
然后将新引用重新插入html内,再将html返回给客户端。
客户端下载完毕后开始加载,遇到css和js引用后会再向服务器发出请求,服务器交给我们自定义的HttpHandler处理,将js和css文件去空格,再利用Filter设置将输出流进行Gzip或Deflate压缩,然后传给客户端,客户端接收后解压缩就ok了~
2.具体实现
那么在什么阶段替换html最好呢?作者给出的答案是PostReleaseRequestState(在 ASP.NET 已完成所有请求事件处理程序的执行并且请求状态数据已存储时发生)。
PostReleaseRequestState是添加filter的最好时机,其他的都太早或太晚,在这个时候完整的response content已经创建完毕,并且页面也已经完整的执行,但是从asp.net的角度看仍需要经过几个模块(详见上一篇文章流程图),我们在这里filer content并且所有的javascript都已经正确的renders。
今天主要跟大家分享一下这部分的代码。
我们创建一个类实现IHttpModule接口
public class HttpModule : IHttpModule
{
void IHttpModule.Dispose()
{
}
void IHttpModule.Init(HttpApplication context)
{
context.PostReleaseRequestState += new EventHandler(context_PostReleaseRequestState);
}
void context_PostReleaseRequestState(object sender, EventArgs e)
{
..........
}
}
复制代码
并且在初始化时注册PostReleaseRequestState事件。
下面是context_PostReleaseRequestState方法代码及个人理解
void context_PostReleaseRequestState(object sender, EventArgs e)
{
//拿到HttpApplication
HttpApplication app = (HttpApplication)sender;
//以请求的绝对路径为基准作为cache的key
string cache = app.Request.Url.AbsoluteUri;
//如果是微软的Ajax,则不做压缩处理 www.zzzyk.com
if (app.Request["HTTP_X_MICROSOFTAJAX"] != null)
return;
string realPath = "";
Configuration settings = null;
// 获取配置文件配置(这里涉及了配置文件类,因为很简单,大家应该能看懂,就不细说了),先从Cache中查找是否存有配置信息,如果有就直接从缓存中取,没有则从Web.config中获取
if (app.Context.Cache["DCCompressModuleConfig"] == null)
{
settings = (Configuration)ConfigurationManager.GetSection("DCWeb/HttpCompress");
app.Context.Cache["DCCompressModuleConfig"] = settings;
}
else
settings = (Configuration)app.Context.Cache["DCCompressModuleConfig"];
if (settings != null)
{
//将配置信息插入缓存
app.Context.Cache.Insert("DCCompressModuleConfig", settings);
//如果配置中没有设置CompressionType则不压缩,取值有gzip或deflate
if (settings.CompressionType == CompressionType.None)
return;
//获取请求文件名称
realPath = app.Request.Path.Remove(0, app.Request.ApplicationPath.Length);
realPath = (realPath.StartsWith("/")) ? realPath.Remove(0, 1) : realPath;
bool isIncludedPath, isIncludedMime;
//是否包含在IncludedPaths(配置中)中
isIncludedPath = (settings.IncludedPaths.Contains(realPath) | settings.IncludedPaths.Contains("~/" + realPath));
//是否为包含的Mime(配置中)类型
isIncludedMime = (settings.IncludedMimeTypes.Contains(app.Response.ContentType));
// 即没有在包含目录中,也没有在允许压缩的Mime中,则不压缩
if (!isIncludedPath && !isIncludedMime)
return;
// 如果包含在ExcludedPaths(不压缩路径,配置中),则不压缩
if (settings.ExcludedPaths.Contains(realPath) | settings.ExcludedPaths.Contains("~/" + realPath))
return;
// 如果包含在ExcludedMimeTypes(不压缩的Mime类型,配置中),则不压缩
if (settings.ExcludedMimeTypes.Contains(app.Response.ContentType))
return;
}
//设置缓存根据Accept-Encoding标头改变输出
//注意如果这里设置了true,那么之前的流程里设置的HttpHeader会失效,需从新设置
app.Context.Response.Cache.VaryByHeaders["Accept-Encoding"] = true;
//拿到浏览器支持的压缩类型(Gzip或Deflate)
string acceptedTypes = app.Request.Headers["Accept-Encoding"];
//如果Response的ContentType是text/html则不进行缓存,否则进行缓存
if (app.Response.ContentType != "text/html")
{
app.Context.Response.Cache.SetCacheability(HttpCacheability.Public);
app.Context.Response.Cache.SetMaxAge(new TimeSpan(7, 0, 0, 0));
app.Context.Response.Cache.SetExpires(DateTime.Now.AddYears(1));
try
{
app.Context.Response.Cache.SetETag(Util.GetMd5Sum(cache));//GetMd5Sum方法在工具类中,不太难,大家可以看下,作用是根据cache值返回一个唯一的值
}
catch (InvalidOperationException)//处理重复设置异常
{
app.Context.Response.AppendHeader("ETag",Util.GetMd5Su
补充:Web开发 , ASP.Net ,