[ASP.NET]利用HttpModule实现动态Web网页内容过滤
来源:互联网 发布:淘宝c店申请企业店铺 编辑:程序博客网 时间:2024/04/28 23:18
目标
实现对Web请求的动态内容进行字符串过滤,比如去掉所有注释和空行(可自行配置),亦可压缩HTML输出流,减小流量消耗。
前言
关于什么是HttpModule及其作用,可自行查找相关文章。本文旨在通过对HttpModule的实际应用,加深对服务器动态处理请求过程的理解。其中参考了网上几位大侠的处理思路,同时结合自身实际,编织出了自己的一套方法。当然肯定有更适合的处理办法,希望有缘能看到这篇文章的朋友们不吝赐教,并且对于其中的不当之处,如能指出,感激不尽。
创建HttpModule
- 开始第一步,向项目中添加一个新类HttpFilterModule,实现IHttpModule类的接口,命名为HttpFilterModule.cs,默认位于App_Code文件夹中。
- 在IHttpModule中需要实现两个接口函数,一个是Init(HttpApplication application),其中以HttpApplication类型作为传入参数,需要具体实现,也是主要实现方法;一个是Dispose(),无参数,这里无需具体实现方法。HttpFilterModule.cs文件内容如下:
using System;using System.Web;using System.Web.Configuration;/// <summary>/// HTTP页面字符串过滤/// </summary>public class HttpFilterModule : IHttpModule{ public HttpFilterModule(){} public void Init(HttpApplication application) { //TODO: 这里实现具体过滤方法 } public void Dispose() { }}
主要HttpModule建立好了,现在需要的是实现方法。不过现在涉及到另一个很重要的对象:Response.Filter,通过IntelliSense的快速信息我们可以看到,Response.Filter对象属于System.IO.Stream对象,解释是“获取或设置一个包装筛选器对象,该对象用于在传输之前修改HTTP实体主体。”因此可以看出通过修改(设置)改对象可达到过滤HTTP实体主体内容的目的。
创建RawFilter过滤器
既然Response.Filter对象属于System.IO.Stream对象,要修改它就需要建立一个原始过滤对象,继承Stream对象,然后重写Stream对象的Write()方法修改其输出对象,最后将重写后的Stream流对象赋给Response.Filter对象即可。具体操作:新建RawFilter.cs文件,默认也位于App_Code文件夹中,双击打开输入以下代码:using System;using System.IO;using System.Text;using System.Text.RegularExpressions;using System.Web;using System.Xml.XPath;/// <summary>/// 自定义原始过滤器,用于处理原始数据流/// </summary>public class RawFilter : Stream{ Stream responseStream; HttpRequest request; long position; StringBuilder responseHtml; /// <summary> /// 原始过滤器 /// </summary> /// <param name="inputStream">输入流</param> public RawFilter(Stream inputStream, HttpRequest httpRequest) { responseStream = inputStream; responseHtml = new StringBuilder(); request = httpRequest; } //关键的点,在HttpResponse 输出内容的时候,输出一定会调用此方法数据,所以要在此方法内截获数据(重写) public override void Write(byte[] buffer, int offset, int count) { string strBuffer = System.Text.UTF8Encoding.UTF8.GetString(buffer, offset, count); //采用正则表达式,检查输入是否有页面结束符</html>,有即表示页面流输出完毕 Regex eof = new Regex("</html>", RegexOptions.IgnoreCase); if (!eof.IsMatch(strBuffer)) { //页面没有输出完毕,继续追加内容 responseHtml.Append(strBuffer); } else { //页面输出已经完毕,截获内容 responseHtml.Append(strBuffer); string finalHtml = responseHtml.ToString(); //谨慎选择注释以下内容,因为网页内容/排版可能会因此而改变 //finalHtml = Regex.Replace(finalHtml, "<!--.*-->", string.Empty, RegexOptions.Compiled | RegexOptions.Multiline); //finalHtml = Regex.Replace(finalHtml, "^\\s*", string.Empty, RegexOptions.Compiled | RegexOptions.Multiline); //finalHtml = Regex.Replace(finalHtml, "\\r\\n", string.Empty, RegexOptions.Compiled | RegexOptions.Multiline); //注释以上内容的原因那里把过滤内容写死了,以后手动修改很麻烦, //因此我另建FilterString()方法通过读取XML配置文件来动态更新过滤方法 finalHtml = FilterString(finalHtml); //过滤字符串 //继续传递要发出的内容写入流 byte[] data = System.Text.UTF8Encoding.UTF8.GetBytes(finalHtml); responseStream.Write(data, 0, data.Length); } } #region 过滤字符串主函数 /// <summary> /// 过滤字符串 /// </summary> /// <param name="InputString">输入源字符串</param> /// <returns>返回已过滤的字符串</returns> protected string FilterString(string InputString) { string configPath = HttpContext.Current.Server.MapPath("~/XML/FilterConfig.xml"); string finalString = InputString; try { if (!System.IO.File.Exists(configPath)) //如果配置文件不存在,则创建默认配置文件 { return finalString; //不存在则原样返回 } else { XPathDocument xpDoc = new XPathDocument(configPath); //载入只读配置文件 XPathNavigator xpNav = xpDoc.CreateNavigator(); XPathNodeIterator xpNt = xpNav.Select("//Rules/FilterRule"); while (xpNt.MoveNext()) { XPathNavigator xpNav2 = xpNt.Current.Clone(); string lookFor = ""; //查询规则,为正则表达式 string sendTo = ""; //重写规则,为正则表达式 XPathNodeIterator xpNt2 = xpNav2.Select("LookFor"); while (xpNt2.MoveNext()) { lookFor = xpNt2.Current.Value; break; } xpNt2 = xpNav2.Select("SendTo"); while (xpNt2.MoveNext()) { sendTo = xpNt2.Current.Value; break; } if (lookFor != string.Empty) { finalString = Regex.Replace(finalString, lookFor, sendTo, RegexOptions.Compiled | RegexOptions.Multiline); } } //END WHILE return finalString; } } catch (Exception ex) { return finalString; } } #endregion #region 实现 Stream 抽象方法 public override bool CanRead{get{return true;}} public override bool CanSeek{ get { return true; }} public override bool CanWrite{ get{ return true; }} public override void Close(){ responseStream.Close(); } public override void Flush(){ responseStream.Flush(); } public override long Length{ get{ return 0; }} public override long Position{ get { return position; } set { position = value; }} public override int Read(byte[] buffer, int offset, int count){ return responseStream.Read(buffer, offset, count);} public override long Seek(long offset, SeekOrigin origin){ return responseStream.Seek(offset, origin); } public override void SetLength(long length) { responseStream.SetLength(length);} #endregion}仔细观察以上代码,整个思路是:
- 该类继承Stream类,注意Stream是抽象类,派生类需要实现它的所有抽象方法,因此最下面的抽象方法实现不能遗漏(虽然看似不参与主要功能);
- 构造函数用于初始化输入流和请求对象;
- 主要方法Write重写了基类中的方法,首先将字节流转换成字符串,通过正则表达式匹配内容来判断页面输出是否完毕,完毕后再整合原始HTML字符串;
- 通过自定义函数对输入HTML字符流进行过滤,主要是在XML配置文件中读取自定义配置节,节点内容是正则表达式的原始匹配字符串和目标字符串,然后进行字符串替换;
- 最后再将字符串转换成字节数组进行Write输出;
<?xml version="1.0" encoding="utf-8" ?><!-- 字符串过滤配置文件 --><FilterConfig> <!-- 过滤规则 --> <Rules> <!-- 注释标记 --> <FilterRule> <LookFor><!--.*--></LookFor> <SendTo></SendTo> </FilterRule> <!-- 空行 --> <FilterRule> <LookFor>^\s*</LookFor> <SendTo></SendTo> </FilterRule> </Rules></FilterConfig>我用正则表达式专门匹配了注释标记和空行,<LookFor>是要匹配的原始字符串,也就是注释标记格式,HTML注释格式为:<!-- --> ,利用正则表达式很方便,这里说个题外话,正则表达式可是个好东西,但是学习起来会有很大的起伏过程,正则表达式测试工具有很多,VS的插件也有,比如RegexTester等,正则表达式很值得探索。<SendTo>配置节内容是匹配之后想要替换成的内容,这里将注释和空行都替换为空字符,也就达到了去掉的目的。
装配RawFilter过滤器
好了,现在要做的就是原始过滤器的装配了,也就是修改(设置)之前的Response.Filter对象。回到HttpFilterModule.cs文件,其中新建自定义函数application_ReleaseRequestState用于在HTTP请求结束后进行过滤(关于请求处理过程各个不同状态及不同功能可查阅相关资料,如BeginRequest在刚开始发送请求时发生),application_ReleaseRequestState函数内容如下:/// <summary>/// 对此HTTP请求处理的过程全部结束/// </summary>/// <param name="sender"></param>/// <param name="e"></param>public void FilterStream(object sender, EventArgs e){ HttpApplication application = (HttpApplication)sender; //针对网页类型过滤 if (application.Response.ContentType.ToLowerInvariant().Contains("text/html")) { //针对ASPX页面进行拦截 string reqPath = application.Request.CurrentExecutionFilePath; if (reqPath.Contains("aspx")) { //装配过滤器 application.Response.Filter = new RawFilter(application.Response.Filter, application.Request); } }}仔细观察以上代码,我只针对后缀名为aspx的文件进行处理,可根据实际情况进行更改。
这样只是完成了一个函数的设计,现在要进行调用,这里就涉及到在什么时候进行调用的问题,关于HttpModule处理请求的不同过程需要了解,这里是要过滤请求完成后的内容,因此需要等待请求结束释放后再进行处理,注意到刚开始建立的HttpFilterModule.cs文件中的TODO(待完成,备忘)标记了麽,是的,在HttpFilterModule实现IHttpModule接口中有个很重要的接口Init()初始化方法,模块启动时将会调用该方法,其中传入的参数是整个HttpApplication对象(拿到了这个,还有什么不能干^_^)。因此,在Init()方法中为应用程序对象(HttpApplication)中的释放请求状态事件ReleaseRequestState添加一个处理事件,也就是application_ReleaseRequestState处理。
using System;using System.Web;using System.Web.Configuration;/// <summary>/// HTTP页面字符串过滤/// </summary>public class HttpFilterModule : IHttpModule{ public HttpFilterModule(){} public void Init(HttpApplication application) { //加入释放请求事件 application.ReleaseRequestState += new EventHandler(FilterStream); } public void Dispose() { }}
配置Web.config文件
整个过程就完成了,捏了一把汗。不过你以为这样就可以成功了麽,那你真是Too young to simple, sometimes naive. 细心地你会问为什么程序知道要执行HttpFilterModule这个模块呢?问的不错,她真的不知道,所以你要告诉她。其实到了这一步就很简单了,但很关键(很容易忘掉),那就是要在Web.config文件中添加相应配置,也就是告诉网站程序,我在这里安了一道门,进出都要从我这里过,并且经过我的审核。具体配置节位于system.web/httpModules中,没有的话需要自行添加。[注:在IIS7及以上版本的服务器中可能需要配置在system.webServer/Modules配置节中,详情请参考IIS帮助说明]<system.web> <httpModules> <add name="HttpFilterModule" type="HttpFilterModule" /> </httpModules></system.web>注意以上name的内容为模块名,也就是新建的类名,如果有命名空间的要改为“命名空间名.模块名”形式。
以上实现[内容过滤]的问题就完成了,现在可以试试运行了,我们来看看效果。
1. 源代码编辑器中原始代码(含空行和注释)
2. 不加HttpModule模块时运行后查看源代码效果(依然含很多空行和注释)
3. 加上HttpModule模块后运行查看源代码效果(所有空行和注释都已消除)
关于去除注释和空格有什么用处,大家可以自己权衡,我只是提供了一种思路和方法,想法是无穷大的,大家有什么好的创意可以交流交流。比如还可以去除标签外所有空白,将网页压缩成就好像一团乱麻一样(看过百度首页源代码的人应该知道)。效果如下:
就像压缩js文件一样,貌似挺酷炫的 [呵呵]
0 0
- [ASP.NET]利用HttpModule实现动态Web网页内容过滤
- ASP.NET下利用HttpModule实现简体中文向繁体中文的自动转换
- Asp.net Mvc中利用ValidationAttribute实现xss过滤
- Asp.net Mvc中利用ValidationAttribute实现xss过滤
- 利用ASP.NET实现web套打
- 利用ASP.NET实现web套打
- asp.net 使用HttpModule对全站输出的动态页面的HTML内容进行修改,不会错乱
- ASP.NET (HttpModule,HttpHandler)
- asp.net httpModule
- asp.net HttpModule类
- ASP.NET中的HttpModule
- ASP.NET (HttpModule,HttpHandler)
- ASP.NET基础 HttpModule
- asp.net httpmodule handler
- 在Asp.net 4.0 中动态注册HttpModule
- ASP.NET 抓取网页内容
- ASP.NET 抓取网页内容
- ASP.NET 抓取网页内容
- 类中的方法引用提示The method check(User) is undefined for the type UserDao解决方法。
- http协议与URL协议详解
- 关于wordpress的一些问题的解决办法
- 欧拉函数相关
- Android高级模糊技术
- [ASP.NET]利用HttpModule实现动态Web网页内容过滤
- C++11新特性学习
- 设置滚动条向下滚动
- (iOS)UITableView只允许部分cell支持滑动删除
- luaJIT
- Android中RelativeLayout的使用
- 专家:墨西哥就取消高铁中标赔1亿 中企或再中标
- 使用GTMBase64编码解码字符串
- socket