为期两个月的培训结束了,学到好多,索性花点时间整理出来,希望能给需要的人一点帮助:)
HTTP模块 (modules)
HTTP模块是实现了System.Web.IHttpModule接口的.NET组件. 这些组件通过注册一些特定的事件将它们自己插入到ASP.net的请求处理管线中. 不管何时事件发生, ASP.net调用相关的HTTP模块以使它们和请求交互.
一个HTTP模块被假定实现了下列的IHttpModule接口方法:
Init: 这个方法允许一个HTTP模块向HttpApplication对象的事件注册它自己的事件处理方法.
Dispose: 这个方法给HTTP模块提供一个机会在对象被垃圾回收前执行一些清理工作.
一个HTTP模块可以注册下列System.Web.HttpApplication对象公开的事件:
AcquireRequestState: 这个事件在ASP.net运行时准备获取当前HTTP请求的会话状态时激发.
AuthenticateRequest: 这个事件在ASP.net运行时准备验证用户的标识时引发.
AuthorizeRequest: 这信事件在ASP.net运行时准备授权用户试图访问的资源时引发.
BeginRequest: 这个事件在ASP.net收到一个新的HTTP请求时引发.
Disposed: 这个事件在当ASP.net结束了HTTP请求的处理时引发.
EndRequest: 这个事件在将响应内容返回给客户之前引发。
Error: 这个事件在处理HTTP请求过程中发生未处理异常时引发。
PostRequestHandlerExecute: 这个事件在HTTP处理器刚刚执行结束后引发。
PreRequestHandlerExecute: 这个事件在ASP.net为HTTP请求执行处理器之前引发。在这个事件之后,ASP.net将会把请求交给适当的HTTP处理器。
PreSendRequestContent: 这个事件在ASP.net发送响应内容给客户之前引发。 这个事件允许我们在它传递给客户端之前修改它的内容。我们可以用这个事件来向页面输出添加对所有页面共用的内容,比如说,一个共用菜单,页头或页脚。
PreSendRequestHeaders: 这个事件在ASP.net发送HTTP响应的头部给客户之前引发。这个事件允许我们在它们传递给客户之前改变头部的内容。 我们可用这个事件来添加cookies及自定义的数据在头字段中。
ReleaseRequestState: 这个事件在ASP.net结束执行所有的请求处理器后发生。
ResolveRequestCache: 这个事件被引发以决定这个请求是否可通过从输出缓存返回内容来满足。这个取决于输出缓存(Output Caching)对于你的应用是怎样设置的。
UpdateRequestCache: 这个事件在ASP.net已经完成了对当前请求的处理并准备把输出内容加入到输出缓存的时候引发, 这个取决于输出缓存对你的应用是怎样设置的。
除了这些事件以外, 还有四个事件我们可以使用。 我们可以通过实现在我们应用程序中的global.asax文件的方法来捕捉这些事件。
* Application_OnStart
这个事件在第一个请求到达Web应用时引发。
* Application_OnEnd
这个事件在应用将要终止之前引发。
* Session_OnStart
这个事件在第一次请求用户会话时引发。
* Session_OnEnd
这个事件在会话会取消或超期时引发。
如何使用 Visual C# .NET 创建 ASP.NET HTTP 模块
实现模块
1. 新建一个名为 MyModule 的 Visual Studio .NET C# 类库项目。
2. 设置一个对 System.Web.dll 程序集的引用。
3. 将以下指令添加到该类中:using System.Web;
4. 重命名 SyncModule.cs 类,然后相应地更改类定义。
5. 实现 IHttpModule 接口。您的类定义应如下所示:public class SyncModule : IHttpModule
6. 决定要预订哪些事件。
本示例使用了 BeginRequest 事件。
7. 按以下方式实现 IHttpModule 接口的 Init 和 Dispose 方法:public void Init(HttpApplication app)
{
app.BeginRequest += new EventHandler(OnBeginRequest);
}
public void Dispose(){ }
8. 按以下方式为事件创建一个委托:public delegate void MyEventHandler(Object s, EventArgs e);
9. 定义一个 MyEventHandler 类型的私有局部变量以记录对该事件的引用:private MyEventHandler _eventHandler = null;
10. 创建一个事件将该委托挂接到 Global.asax 文件中的方法或从 HttpApplication 对象继承的类:public event MyEventHandler MyEvent
{
add { _eventHandler += value; }
remove { _eventHandler -= value; }
}
11. 创建 OnBeginRequest 方法,该方法挂接到 HttpApplication 的 BeginRequest 事件:public void OnBeginRequest(Object s, EventArgs e)
{
HttpApplication app = s as HttpApplication;
app.Context.Response.Write("Hello from OnBeginRequest in custom module.<br>");
if(_eventHandler!=null)
_eventHandler(this, null);
}
12. 编译该项目。
部署模块
1. 在 C:/Inetpub/Wwwroot 下新建一个名为 Module 的目录。
2. 在新创建的 Module 目录下创建一个名为 Bin 的子目录。这样,其路径为 C:/Inetpub/Wwwroot/Module/Bin。
3. 将 MyModule.dll 从项目的 Bin/Debug 目录复制到 C:/Inetpub/Wwwroot/Module/Bin 目录。
4. 按照下列步骤将新的 Module 目录标记为一个 Web 应用程序:a. 打开 Internet 服务管理器。
b. 右键单击 Module 目录,然后单击属性。
c. 在目录选项卡上,单击创建。
d. 单击确定以关闭 Module 属性对话框。
配置系统
1. 在 C:/Inetpub/Wwwroot/Module/ 目录下,新建一个名为 Web.config 的文件。
2. 将以下文本粘贴到 Web.config 文件中:<configuration>
<system.web>
<httpModules>
<add name="MyModule" type="MyModule.SyncModule, MyModule" />
</httpModules>
</system.web>
</configuration>
测试模块
1. 在 C:/Inetpub/Wwwroot/Module 目录下,新建一个名为 Test.aspx 的 .aspx 文件。
2. 将以下文本粘贴到 Test.aspx 文件中:<%@Page Language="C#"%>
<% Response.Write("Hello from Test.aspx.<br>"); %>
3. 在 C:/Inetpub/Wwwroot/Module 目录下,创建一个 Global.asax 文件。
4. 将以下代码粘贴到 Global.asax 文件中:<%@ Import Namespace="MyModule" %>
<script language="C#" runat=server >
protected void MyModule_OnMyEvent(Object src, EventArgs e)
{
Context.Response.Write("Hello from MyModule_OnMyEvent called in Global.asax.<br>");
}
</script>
5. 请求 Test.aspx 页。应该会看到以下文本行:
Hello from OnBeginRequest in custom module.
Hello from MyModule_OnMyEvent called in Global.asax.
Hello from Test.aspx.
当然部署的时候方法很多,大家可以自己试着来,实现的步骤大致就是这样的。
在培训中,我们遇到了这样的问题,需要统计某网站每天每关键字出现的次数
这样就需要在每次服务器端返回内容的时候,在http module模块下实现对html内容的捕获,这样就用到了HttpResponse 对象的过滤器,因为貌似只有同这种方法,才能有效的获取输出的内容并对它进行修改。
具体做法如下:
1.首先要建立一个简易过滤器。
代码如下:
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Web;
/**//// <summary>
/// 定义原始数据EventArgs,便于在截获完整数据后,由事件传递数据
/// </summary>
public class RawDataEventArgs : EventArgs
{
private string sourceCode;
public RawDataEventArgs(string SourceCode)
{
sourceCode = SourceCode;
}
public string SourceCode
{
get { return sourceCode; }
set { sourceCode = value; }
}
}
//自定义过滤器
public class RawFilter : Stream
{
Stream responseStream;
long position;
StringBuilder responseHtml;
/**//// <summary>
/// 当原始数据采集成功后激发。
/// </summary>
public event EventHandler<RawDataEventArgs> OnRawDataRecordedEvent;
public RawFilter(Stream inputStream)
{
responseStream = inputStream;
responseHtml = new StringBuilder();
}
//实现Stream 虚方法
Filter Overrides#region Filter Overrides
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
//关键的点,在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();
//激发数据已经获取事件
OnRawDataRecordedEvent(this, new RawDataEventArgs(finalHtml));
//继续传递要发出的内容写入流
byte[] data = System.Text.UTF8Encoding.UTF8.GetBytes(finalHtml);
responseStream.Write(data, 0, data.Length);
}
}
}
至此,过滤器定义完毕了,接下来还需要把这个过滤器装配到HttpResponse 对象中。
为了能够截获整站的aspx 页面输出的内容,我们可以定义一个HttpModule 来完成。
代码如下:
using System;
using System.Web;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;
public class HttpRawDataModule : IHttpModule
{
IHttpModule 成员#region IHttpModule 成员
public void Dispose()
{
}
public void Init(HttpApplication context)
{
//绑定事件,在对此请求处理过程全部结束后进行过滤操作
context.ReleaseRequestState += new EventHandler(context_ReleaseRequestState);
}
#endregion
/**//// <summary>
/// 对此HTTP请求处理的过程全部结束
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void context_ReleaseRequestState(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
//这里需要针对ASPX页面进行拦截,测试发现如果不这么做,Wap 访问站点图片容易显示为X,奇怪
string[] temp = application.Request.CurrentExecutionFilePath.Split('.');
if (temp.Length > 0 && temp[temp.Length - 1].ToLower() == "aspx")
{
//装配过滤器
application.Response.Filter = new RawFilter(application.Response.Filter);
//绑定过滤器事件
RawFilter filter = (RawFilter)application.Response.Filter;
filter.OnRawDataRecordedEvent += new EventHandler<RawDataEventArgs>(filter_OnRawDataRecordedEvent);
}
}
/**//// <summary>
/// 当原始数据采集到以后,入库
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void filter_OnRawDataRecordedEvent(object sender, RawDataEventArgs e)
{
string allcode = e.SourceCode;
string keyword = "Default";
Regex check = new Regex(keyword);
MatchCollection m = check.Matches(allcode);
int count = 0;
count = m.Count;
}
}
HttpModule 准备完毕,也装配上了过滤器,接下来还需要在配置文件中配置HttpModules配置节 ,把自定义的HttpModule 加入到HTTP处理管道中。
在Web.config 中增加配置节如下:
<system.web>
<httpModules>
<add name="RawDataModule" type="HttpRawDataModule"/>
</httpModules>
</system.web>