ASP.Net 重写IHttpModule 来拦截 HttpApplication 实现HTML资源压缩和空白过滤
务实直接上代码:
1. 重写FilterModule.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Text.RegularExpressions;
using System.IO.Compression; namespace Compress.FilterModule
{
public class FilterModule : IHttpModule
{
public void Dispose()
{
//
} /// <summary>
/// Init method is only used to register the desired event
/// </summary>
/// <param name="context"></param>
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
//context.EndRequest += new EventHandler(context_EndRequest);
//context.PostRequestHandlerExecute += new EventHandler(context_PostRequestHandlerExecute);
} /// <summary>
/// Handles the BeginRequest event of the context control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
void context_BeginRequest(object sender, EventArgs e)
{
HttpApplication app = sender as HttpApplication;
HttpContext context = app.Context;
if (context.CurrentHandler is System.Web.UI.Page)
{
bool isPage = context.CurrentHandler.IsReusable;
}
if (app.Request.RawUrl.Contains(".aspx") || app.Request.RawUrl.EndsWith("/"))
{
// HttpContext context = app.Context;
HttpRequest request = context.Request;
string acceptEncoding = request.Headers["Accept-Encoding"];
HttpResponse response = context.Response;
if (!string.IsNullOrEmpty(acceptEncoding))
{
acceptEncoding = acceptEncoding.ToUpperInvariant();
if (acceptEncoding.Contains("GZIP"))
{
//var straem = new GZipStream(response.Filter, CompressionMode.Compress);
response.Filter = new CompressWhitespaceFilter(response.Filter, CompressOptions.GZip);
response.AppendHeader("Content-encoding", "gzip");
}
else if (acceptEncoding.Contains("DEFLATE"))
{
response.Filter = new CompressWhitespaceFilter(context.Response.Filter, CompressOptions.Deflate);
response.AppendHeader("Content-encoding", "deflate");
}
}
response.Cache.VaryByHeaders["Accept-Encoding"] = true;
}
} // Test
//void context_BeginRequest(object sender, EventArgs e)
//{
// HttpApplication application = (HttpApplication)sender;
// HttpContext context = application.Context;
// context.Response.ContentType = "text/html";
// context.Response.Charset = "GB2312";
// context.Response.ContentEncoding = Encoding.Default;
// context.Response.Write("<h1 style='color:#00f'>Treatment from HttpModule,Begin...</h1><hr>");
//} // Test
//void context_EndRequest(object sender, EventArgs e)
//{
// HttpApplication application = (HttpApplication)sender;
// HttpContext context = application.Context;
// context.Response.ContentType = "text/html";
// context.Response.Charset = "GB2312";
// context.Response.ContentEncoding = Encoding.Default;
// context.Response.Write("<hr><h1 style='color:#f00'>Treatment from HttpModule,End...</h1>");
//} }
}
2. 处理压缩和匹配自定义过滤 CompressWhitespaceFilter.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.IO.Compression; namespace Compress.ModuleDemo
{
public enum CompressOptions
{
GZip,
Deflate,
None
} public class CompressWhitespaceFilter : Stream
{
StringBuilder responseHtml;
const string _cssPattern = "(?<HTML><link[^>]*href\\s*=\\s*[\\\"\\']?(?<HRef>[^\"'>\\s]*)[\\\"\\']?[^>]*>)";
const string _jsPattern = "(?<HTML><script[^>]*src\\s*=\\s*[\\\"\\']?(?<SRC>[^\"'>\\s]*)[\\\"\\']?[^>]*></script>)"; private HttpApplication app;
public HttpApplication App
{
get { return app; }
set { app = value; }
} private GZipStream _contentGZip;
private DeflateStream _content_Deflate;
private Stream _content;
private CompressOptions _options;
private bool disposed = false; private CompressWhitespaceFilter() { }
public CompressWhitespaceFilter(Stream content, CompressOptions options)
{ responseHtml = new StringBuilder();
if (options == CompressOptions.GZip)
{
this._contentGZip = new GZipStream(content, CompressionMode.Compress, true);
this._content = this._contentGZip;
}
else if (options == CompressOptions.Deflate)
{
this._content_Deflate = new DeflateStream(content, CompressionMode.Compress, true);
this._content = this._content_Deflate;
}
else
{
this._content = content;
}
this._options = options;
} public override bool CanRead
{
get { return this._content.CanRead; }
} public override bool CanSeek
{
get { return this._content.CanSeek; }
} public override bool CanWrite
{
get { return this._content.CanWrite; }
} /// <summary>
/// When overriding in a derived class, all buffers of the stream are cleared and all buffer data is written to the underlying device
/// </summary>
public override void Flush()
{
this._content.Flush();
//Test
//this._content.Dispose();
//this._contentGZip.Dispose();
} /// <summary>
/// 重写Dispose方法,释放派生类自己的资源,并且调用基类的Dispose方法
/// 使Gzip把缓存中余下的内容全部写入MemoryStream中,因为只有在Gzip流释放之后才能去承载对象中读取数据或判断数据大小
/// </summary>
/// <param name="disposing"></param>
protected override void Dispose(bool disposing)
{
if (!this.disposed)
{
try
{
if (disposing)
{
// Release the managed resources you added in this derived class here.
//xx.Dispose();
} // Release the native unmanaged resources you added in this derived class here.
// xx.Close() //if (_contentGZip != null)
// _contentGZip.Close(); //if (_content_Deflate != null)
// _content_Deflate.Close(); this._content.Close();
this.disposed = true;
}
finally
{
// Call Dispose on your base class.
base.Dispose(disposing);
}
}
} public override long Length
{
get { return this._content.Length; }
} public override long Position
{
get
{
return this._content.Position;
}
set
{
this._content.Position = value;
}
} public override int Read(byte[] buffer, int offset, int count)
{
return this._content.Read(buffer, offset, count);
} public override long Seek(long offset, SeekOrigin origin)
{
return this._content.Seek(offset, origin);
} public override void SetLength(long value)
{
this._content.SetLength(value);
} public override void Write(byte[] buffer, int offset, int count)
{
byte[] data = new byte[count + ];
Buffer.BlockCopy(buffer, offset, data, , count);
string s = System.Text.Encoding.UTF8.GetString(data);
s = Regex.Replace(s, "^\\s*", string.Empty, RegexOptions.Compiled | RegexOptions.Multiline);
s = Regex.Replace(s, "\\r\\n", string.Empty, RegexOptions.Compiled | RegexOptions.Multiline);
s = Regex.Replace(s, "<!--*.*?-->", string.Empty, RegexOptions.Compiled | RegexOptions.Multiline);
byte[] outdata = System.Text.Encoding.UTF8.GetBytes(s);
this._content.Write(outdata, , outdata.GetLength());
} /// <summary>
/// Replcase stylesheet links with ones pointing to HttpHandlers that compress and cache the css
/// </summary>
/// <param name="html"></param>
/// <returns></returns>
public string ReplaceCss(string html)
{
// create a list of the stylesheets
List<string> stylesheets = new List<string>();
// create a dictionary used for combining css in the same directory
Dictionary<string, List<string>> css = new Dictionary<string, List<string>>(); // create a base uri which will be used to get the uris to the css
Uri baseUri = new Uri(app.Request.Url.AbsoluteUri); // loop through each match
foreach (Match match in Regex.Matches(html, _cssPattern, RegexOptions.IgnoreCase))
{
// this is the enire match and will be used to replace the link
string linkHtml = match.Groups[].Value;
// this is the href of the link
string href = match.Groups[].Value; // get a uri from the base uri, this will resolve any relative and absolute links
Uri uri = new Uri(baseUri, href);
string file = "";
// check to see if it is a link to a local file
if (uri.Host == baseUri.Host)
{
// check to see if it is local to the application
if (uri.AbsolutePath.ToLower().StartsWith(app.Context.Request.ApplicationPath.ToLower()))
{
// this combines css files in the same directory into one file (actual combining done in HttpHandler)
int index = uri.AbsolutePath.LastIndexOf("/");
string path = uri.AbsolutePath.Substring(, index + );
file = uri.AbsolutePath.Substring(index + );
if (!css.ContainsKey(path))
css.Add(path, new List<string>());
css[path].Add(file + (href.Contains("?") ? href.Substring(href.IndexOf("?")) : ""));
// replace the origianl links with blanks
html = html.Replace(linkHtml, "");
// continue to next link
continue;
}
else
file = uri.AbsolutePath + uri.Query;
}
else
file = uri.AbsoluteUri;
string newLinkHtml = linkHtml.Replace(href, "css.axd?files=" + file); // just replace the link with the new link
html = html.Replace(linkHtml, newLinkHtml);
} StringBuilder link = new StringBuilder();
link.AppendLine("");
foreach (string key in css.Keys)
{
link.AppendLine(string.Format("<link href='{0}css.axd?files={1}' type='text/css' rel='stylesheet' />", key, string.Join(",", css[key].ToArray()))); } // find the head tag and insert css in the head tag
int x = html.IndexOf("<head");
int num = ;
if (x > -)
{
num = html.Substring(x).IndexOf(">");
html = html.Insert(x + num + , link.ToString());
}
return html;
} /// <summary>
/// Replcase javascript links with ones pointing to HttpHandlers that compress and cache the javascript
/// </summary>
/// <param name="html"></param>
/// <returns></returns>
public string ReplaceJS(string html)
{
// if the javascript is in the head section of the html, then try to combine the javascript into one
int start, end;
if (html.Contains("<head") && html.Contains("</head>"))
{
start = html.IndexOf("<head");
end = html.IndexOf("</head>");
string head = html.Substring(start, end - start); head = ReplaceJSInHead(head); html = html.Substring(, start) + head + html.Substring(end);
} // javascript that is referenced in the body is usually used to write content to the page via javascript,
// we don't want to combine these and place them in the header since it would cause problems
// or it is a WebResource.axd or ScriptResource.axd
if (html.Contains("<body") && html.Contains("</body>"))
{
start = html.IndexOf("<body");
end = html.IndexOf("</body>");
string head = html.Substring(start, end - start); head = ReplaceJSInBody(head); html = html.Substring(, start) + head + html.Substring(end);
} return html;
} /// <summary>
/// Replaces the js in the head tag. (see ReplaceCss for comments)
/// </summary>
/// <param name="html"></param>
/// <returns></returns>
public string ReplaceJSInHead(string html)
{
List<string> javascript = new List<string>();
Dictionary<string, List<string>> js = new Dictionary<string, List<string>>(); Uri baseUri = new Uri(app.Request.Url.AbsoluteUri);
foreach (Match match in Regex.Matches(html, _jsPattern, RegexOptions.IgnoreCase))
{
string linkHtml = match.Groups[].Value;
string src = match.Groups[].Value; Uri uri = new Uri(baseUri, src);
if (!Path.GetExtension(uri.AbsolutePath).Equals("js") && uri.AbsolutePath.Contains("WebResource.axd"))
continue;
if (uri.Host == baseUri.Host)
{
if (uri.AbsolutePath.ToLower().StartsWith(app.Context.Request.ApplicationPath.ToLower()))
{
int index = uri.AbsolutePath.LastIndexOf("/");
string path = uri.AbsolutePath.Substring(, index + );
string file = uri.AbsolutePath.Substring(index + );
if (!js.ContainsKey(path))
js.Add(path, new List<string>());
js[path].Add(file + (src.Contains("?") ? src.Substring(src.IndexOf("?")) : ""));
}
else
javascript.Add(uri.AbsolutePath + uri.Query); }
else
javascript.Add(uri.AbsoluteUri);
html = html.Replace(linkHtml, "");
} int x = html.IndexOf("<head");
int num = html.Substring(x).IndexOf(">");
string link = ""; foreach (string key in js.Keys)
{
link = string.Format("<script src='{0}js.axd?files={1}' type='text/javascript' ></script>", key, string.Join(",", js[key].ToArray()));
html = html.Insert(x + num + , link + Environment.NewLine); }
if (javascript.Count > )
{
link = string.Format("<script src='js.axd?files={0}' type='text/javascript' /></script>", string.Join(",", javascript.ToArray()));
html = html.Insert(x + num + , link + Environment.NewLine);
}
return html;
} /// <summary>
/// Replaces the js in the body. (see ReplaceCss for comments)
/// </summary>
/// <param name="html"></param>
/// <returns></returns>
public string ReplaceJSInBody(string html)
{
Uri baseUri = new Uri(app.Request.Url.AbsoluteUri);
foreach (Match match in Regex.Matches(html, _jsPattern, RegexOptions.IgnoreCase))
{
string linkHtml = match.Groups[].Value;
string src = match.Groups[].Value; Uri uri = new Uri(baseUri, src);
if (!uri.AbsolutePath.EndsWith(".js") && !uri.AbsolutePath.Contains("WebResource.axd"))
continue;
string file = "";
string path = "";
if (uri.Host == baseUri.Host)
{
if (uri.AbsolutePath.ToLower().StartsWith(app.Context.Request.ApplicationPath.ToLower()))
{
int index = uri.AbsolutePath.LastIndexOf("/");
path = uri.AbsolutePath.Substring(, index + );
file = uri.AbsolutePath.Substring(index + ) + (src.Contains("?") ? src.Substring(src.IndexOf("?")) : "");
}
else
file = uri.AbsolutePath + uri.Query;
}
else
file = uri.AbsoluteUri;
string newLinkHtml = linkHtml.Replace(src, path + "js.axd?files=" + file);
html = html.Replace(linkHtml, newLinkHtml);
}
return html;
} }
}
在这里需要注意的是对GZIP 的释放,否则流数据会读取不到:
/// <summary>
/// 重写Dispose方法,释放派生类自己的资源,并且调用基类的Dispose方法
/// 使Gzip把缓存中余下的内容全部写入MemoryStream中,因为只有在Gzip流释放之后才能去承载对象中读取数据或判断数据大小
/// </summary>
/// <param name="disposing"></param>
protected override void Dispose(bool disposing)
{
if (!this.disposed)
{
try
{
if (disposing)
{
// Release the managed resources you added in this derived class here.
//xx.Dispose();
} // Release the native unmanaged resources you added in this derived class here.
// xx.Close() //if (_contentGZip != null)
// _contentGZip.Close(); //if (_content_Deflate != null)
// _content_Deflate.Close(); this._content.Close();
this.disposed = true;
}
finally
{
// Call Dispose on your base class.
base.Dispose(disposing);
}
}
}
对于C#非托管资源释放(Finalize/Dispose)方法理解:
http://www.cnblogs.com/lzhdim/archive/2009/11/04/1595845.html
ASP.Net 重写IHttpModule 来拦截 HttpApplication 实现HTML资源压缩和空白过滤的更多相关文章
- 使用rewrite 让php 实现类似asp.net 的IHttpModule 进行带参数js文件的参数获取
asp.net 的IHttpModule 接口具有很大的作用,我们可以使用实现的模块进行全局的控制,但是在学习php 的过程中也想实现类似的功能,查找php 的文档,自己没有找到, 但是我们大家应该知 ...
- asp.net core 使用中间件拦截请求和返回数据,并对数据进行加密解密。
原文:asp.net core 使用中间件拦截请求和返回数据,并对数据进行加密解密. GitHub demo https://github.com/zhanglilong23/Asp.NetCore. ...
- IIS7 ASP.NET 未被授权访问所请求的资源
IIS7 ASP.NET 未被授权访问所请求的资源 ASP.NET 未被授权访问所请求的资源.请考虑授予 ASP.NET 请求标识访问此资源的权限. ASP.NET 有一个在应用程序没有模拟时使用的基 ...
- ASP.NET 未被授权访问所请求的资源。请考虑授予 ASP.NET 请求标识访问此资源的权限
开发了一个导入TXT文件的功能,执行过程中出错.提示:.....ASP.NET 未被授权访问所请求的资源.请考虑授予 ASP.NET 请求标识访问此资源的权限.ASP.NET 有一个在应用程序没有模拟 ...
- asp.net实现IHttpModule接口注意事项
IHttpModule向实现类提供模块初始化和处置事件. IHttpModule包含兩個方法: public void Init(HttpApplication context);public voi ...
- asp.net 使用IHttpModule 做权限检查 登录超时检查(转)
IHttpModule 权限 检查 登录超时检查 这样就不需要每个页面都做一次检查 也不需要继承任何父类. using System;using System.Collections.Generic; ...
- ASP.NET MVC 异常Exception拦截
一.前言 由于客户端的环境不一致,有可能会造成我们预计不到的异常错误,所以在项目中,友好的异常信息提示,是非常重要的.在asp.net mvc中实现异常属性拦截也非常简单,只需要继承另一个类(Syst ...
- ASP.NET MVC 异常Exception拦截器Fillter
异常信息的处理在程序中非常重要, 在asp.net mvc中提供异常属性拦截器进行对异常信息的处理,异常拦截器也没有什么的,只是写一个类,继承另一个类(System.Web.Mvc.FilterAtt ...
- ASP.NET MVC的Action拦截器(过滤器)ActionFilter
有时项目要进行客户端请求(action)进行拦截(过滤)验证等业务,可以使用拦截器进行实现,所谓的action拦截器也没有什么的,只是写一个类,继承另一个类(System.Web.Mvc.Filter ...
随机推荐
- linux下 文件IO 相关
linux下操作文件或设备,需要一个文件描述符 file descriptor,fd 来引用.fd是一个非负整数,实际上是一个索引值,指向文件的记录表,对文件的操作都需要fd.默认的几个:标准输入流 ...
- hive编程指南--employees表数据定义
hive编程指南中有个employees表,默认的分隔符比較繁杂,编辑起来不太方便(普通编辑器编辑的控制字符^A等被当成字符串处理了,没有起到分隔符的作用). 收集的解决方式例如以下: http:// ...
- Codeforces Round #350 (Div. 2)解题报告
codeforces 670A. Holidays 题目链接: http://codeforces.com/contest/670/problem/A 题意: A. Holidays On the p ...
- IOS中的ViewController 的loadView、viewDidLoad、viewDidUnload
由init.loadView.viewDidLoad.viewDidUnload.dealloc的关系说起: 1 init方法 在init方法中实例化必要的对象(遵从LazyLoad思想) init方 ...
- WOJ 1020
#include<stdio.h> #include<stdlib.h> int main() { int n,m; int *num,*link; int i,j,t,k=0 ...
- 让你提前知道软件开发(22):shell脚本文件操作
文章1部分 再了解C语言 shell脚本中的文件操作 [文章摘要] 编写shell脚本时,经常会涉及到对文件的操作,比方从文件里读取一行数据.向文件追加一行数据等. 完毕文件读写操作的方法有非常多,了 ...
- 最经常使用的两种C++序列化方案的使用心得(protobuf和boost serialization)
导读 1. 什么是序列化? 2. 为什么要序列化?优点在哪里? 3. C++对象序列化的四种方法 4. 最经常使用的两种序列化方案使用心得 正文 1. 什么是序列化? 程序猿在编写应用程序的时候往往须 ...
- [置顶] ※数据结构※→☆线性表结构(list)☆============双向链表结构(list double)(三)
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱.所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点. ~~~~~~~~~~~~ ...
- jeecg 3.5.2 新版本号4种首页风格 【经典风格,shortcut风格,ACE bootstrap风格,云桌面风格】
[1]经典风格: [2]Shortcut风格: [3]ACE bootsrap风格: [4]云桌面风格: [5]自己定义图表 watermark/2/text/aHR0cDovL2Jsb2cuY3Nk ...
- 以todomvc为例分析knockout、backbone和angularjs
一.整体结构 项目github地址https://github.com/tastejs/todomvc/ 排除通用的css样式文件和引用的js库文件,仅看html和js 1.1 knockoutjs版 ...