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 ...
随机推荐
- Jquery事件的连接
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- uva--562Dividing coins +dp
题意: 给定一堆硬币,然后将他们分成两部分,使得两部分的差值最小;输出这个最小的差值. 思路: 想了好久都没想到一个合适的状态转移方程.后面看了别人的题解后,才知道能够转成背包问题求解. 我们将全部的 ...
- JSF之经常使用注解
@ManagedBean 以托管 bean 的形式注冊一个类实例.然后将其放入到使用当中一个 @...Scoped 凝视指定的范围内.假设没有指定不论什么范围.JSF 将把此 bean 放入请求范围. ...
- java中Hashtable中的t为什么是小写(转)
因为在很多年前刚学java的时候用到Hashtable的时候比较好奇为什么第二个t是小写,这不符合sun的风格啊,整个jdk都是标准驼峰,于是带着这个疑问翻过 很多书,看多很多资料,最后的结论是: H ...
- Linux下一个简单的日志系统的设计及其C代码实现
1.概述 在大型软件系统中,为了监测软件运行状况及排查软件故障,一般都会要求软件程序在运行的过程中产生日志文件.在日志文件中存放程序流程中的一些重要信息, 包括:变量名称及其值.消息结构定义.函数返回 ...
- Oracle控制文件操作
控制文件是连接instance和 database的纽带.记录了database的结构信息. 控制文件是1个2进制文件.记录的是当前database的状态. 控制文件可以有多个,在参数文件中通过con ...
- 使用Java7提供Fork/Join框架
在Java7在.JDK它提供了多线程开发提供了一个非常强大的框架.这是Fork/Join框架.这是原来的Executors更多 进一步,在原来的基础上添加了并行分治计算中的一种Work-stealin ...
- [置顶] iframe使用总结(实战)
说在前面的话,iframe是可以做很多事情的. 例如: a>通过iframe实现跨域; b>使用iframe解决IE6下select遮挡不住的问题 c>通过iframe解决Ajax的 ...
- Keil - 编译错误总结 01
Keil 编译 STM32project,出现下述错误. 并且. Options for Target -> Output - Browse Information 选项无法勾选. ...
- BaiduMap_SDK_DEMO_3.0.0_for_Xamarin.Android_by_imknown
2.4.2 已稳定, 同一时候已经放置到分支/Release 2.4.2了. 3.0.0 已开发完毕, 可是不推荐大家用于项目中, 请观望或者自己进一步调试. 个人感觉尽管3.0.0简化了开发, 可是 ...