asp.net管道模型学习(二):HttpModule
上一篇文章《asp.net管道模型学习(一):Http请求处理流程》学习了Http请求处理流程,这篇文章介绍HttpModule。
HttpModule是什么
在Http请求处理过程中,请求会前后两次通过一系列的HttpModule,这些Module对Http请求具有完全控制权,可以分别实现某个工作前的事情和某个工作后的事情。那么HttpModule是什么?HttpModule是实现了IHttpModule接口的程序集。在asp.net中事件可以分成三个级别:应用程序级事件、页面级事件、控件级事件,事件的触发分别与应用程序周期、页面周期、控件周期密切相关,而HttpModule是与应用程序事件密切相关的。通过HttpModule在Http请求管道中注册了期望对应用程序事件做出反应的方法,在相应事件触发的时候,便会调用这个HttpModule注册的方法,实际工作在这些方法中执行。
asp.net本身自带了许多HttpModule,在操作系统文件夹C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config中找到web.config:

打开该配置文件,找到HttpModules节点就是系统配置好的HttpModule:
<httpModules> <add name="OutputCache" type="System.Web.Caching.OutputCacheModule" /> <add name="Session" type="System.Web.SessionState.SessionStateModule" /> <add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" /> <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" /> <add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule" /> <add name="RoleManager" type="System.Web.Security.RoleManagerModule" /> <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" /> <add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule" /> <add name="AnonymousIdentification" type="System.Web.Security.AnonymousIdentificationModule" /> <add name="Profile" type="System.Web.Profile.ProfileModule" /> <add name="ErrorHandlerModule" type="System.Web.Mobile.ErrorHandlerModule, System.Web.Mobile, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> <add name="ServiceModel" type="System.ServiceModel.Activation.HttpModule, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" /> <add name="ScriptModule-4.0" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> </httpModules>
name是HttpModule名称,type表示程序集。
实现自己的HttpModule
IHttpModule接口包含两个方法:
//
// 摘要:
// 向实现类提供模块初始化和处置事件。
public interface IHttpModule
{
//
// 摘要:
// 处置由实现 System.Web.IHttpModule 的模块使用的资源(内存除外)。
void Dispose();
//
// 摘要:
// 初始化模块,并使其为处理请求做好准备。
//
// 参数:
// context:
// 一个 System.Web.HttpApplication,它提供对 ASP.NET 应用程序内所有应用程序对象的公用的方法、属性和事件的访问
void Init(HttpApplication context);
}
Init:是入口方法,该方法的参数是HttpApplication对象,在Http处理流程介绍的文章中该对象,HttpRuntime将请求转交给HttpApplication(表示Web应用程序),HttpApplication创建针对Http请求的HttpContext对象,这些对象包含如HttpRequest、HttpResponse等对象,这些对象在程序中可以通过上下文类进行访问。我们要在这个方法内注册HttpApplication对象暴露给客户端的事件,事件处理程序交给另外的方法。
过程可以这样理解:当网站第一个资源被请求时,asp.net会创建HttpApplication实例,它代表站点应用程序,同时会创建所有在Web.Config中注册过的Module(创建Module实例时调用Init方法),
在Init方法内,对要做出响应的HttpApplication暴露出的事件进行注册。HttpApplication在其应用程序周期中触发各类事件,触发事件的时候调用Module在其Init方法中注册过的方法。
Dispose:可以在垃圾回收之前进行一些清理工作。
在Http请求
下面演示自己实现一个HttpModule,然后注册运行。
新建一个类库,名字就叫Web.Core,在该类库中新建一个类GlobalModule,继承IHttpModule,实现接口的方法:
public class GlobalModule : IHttpModule
{
public event EventHandler GlobalModuleEvent;
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
context.EndRequest += new EventHandler(context_EndRequest);
}
public void context_BeginRequest(object sender,EventArgs e)
{
HttpApplication appliction = (HttpApplication)sender;
HttpContext context= appliction.Context;
string extension=Path.GetExtension(context.Request.Url.AbsoluteUri);
if (string.IsNullOrWhiteSpace(extension) && !context.Request.Url.AbsolutePath.Contains("Verify"))
{
context.Response.Write($"来自GlobalModule中context_BeginRequest {DateTime.Now.ToString()} ");
}
if(GlobalModuleEvent!=null)
{
GlobalModuleEvent.Invoke(this,e);
}
}
public void context_EndRequest(object sender, EventArgs e)
{
HttpApplication appliction = (HttpApplication)sender;
HttpContext context = appliction.Context;
string extension = Path.GetExtension(context.Request.Url.AbsoluteUri);
if (string.IsNullOrWhiteSpace(extension) && !context.Request.Url.AbsolutePath.Contains("Verify"))
{
context.Response.Write($"来自GlobalModule中context_EndRequest {DateTime.Now.ToString()} ");
}
}
public void Dispose()
{
}
}
我们通过这个HttpApplication对象分别给管道中BeginRequest事件和EndRequest事件注册了方法,在两个方法中都像响应流中输出字符串。
HttpModule写好了,我们需要注册它,只需要将Web.Core的dll文件拷贝到的网站项目中的bin目录下即可,本次直接在网站项目中引用该dll,在项目中的配置文件web.config中的system.webServer节点下modules中添加一项:
<add name="GlobalModule" type="Web.Core.Pipeline.GlobalModule,Web.Core" />
name表示HttpModule名称,可以和具体的类名不一样,可以通过程序获取HttpModuleCollection集合,再通过这个name获取到具体的HttpModule对象,下面会讲到。type由逗号分隔的两部分组成,第一部分表示命名空间+类名,第二部分表示程序集。
这里要特别说明下,system.web节点针对的是VS2013之前/IIS7.0之前和经典模式,而system.webServer节点针对的是VS2013及以后/IIS7.0之后的集成模式。
在该项目中访问任意一个页面都会输出字符串:

可以理解HttpModule就是对HttpApplication的扩展。
展示所有的HttpModule
我们可以通过获取当前上下文的HttpApplication的实例的Modules属性来获取到HttpModuleCollection集合,现在我们全部在页面中展示出来:
/// <summary>
/// 展示请求的Module
/// </summary>
/// <returns></returns>
public ActionResult ShowModules()
{
List<SysModuleInfo> moduleList = new List<SysModuleInfo>();
HttpApplication application = HttpContext.ApplicationInstance;//获取当前上下文的HttpApplication实例
int i = 1;
foreach (var moduleName in application.Modules.AllKeys)
{
moduleList.Add(new SysModuleInfo
{
Id = i++,
Name = moduleName,
TypeName = application.Modules[moduleName].ToString()
});
}
return View(moduleList);
}
在视图页中添加:
@model IEnumerable<SysModuleInfo>
@{
ViewBag.Title = "ShowModules";
}
<h2>ShowModules</h2>
<table class="table">
<tr>
<th>序号</th>
<th>名称</th>
<th>详细类型名称</th>
</tr>
@foreach (SysModuleInfo item in Model)
{
<tr>
<td>
@item.Id
</td>
<td>
@item.Name
</td>
<td>
@item.TypeName
</td>
</tr>
}
</table>
输出:
默认是18个Module,上面的GlobalModule是我们自己实现的Module。
1、OutputCacheModule完成Asp.net的输出缓存管理工作:
OutputCacheModule的配置参数通过system.web配置元素的caching子元素的outputCache元素进行定义。当启用输出缓存之后(启用还是通过配置文件,下同),
OutputCacheModule将注册HttpApplication的ResolveRequestCache和UpdateRequestCache两个事件完成输出缓存的管理。
2、SessionStateModule完成Session的管理工作:
这个Module的配置参数通过配置文件中的system.web配置元素的sessionState子元素进行配置。当启用Session状态管理之后,
SessionStateModule将注册HttpApplication的AcquireRequestState、ReleaseRequestState、EndRequest三个事件完成Session状态的管理工作。
3、ProfileModule提供个性化数据管理:
这是一个自定义的类似于Session的会话状态管理,但是个性化数据的读取和保存可以由程序员完全控制,并且提供了强类型的数据访问方式。
这个Module的配置参数在system.web的子元素profile中进行说明。当启用了个性化数据管理之后,Module将注册HttpApplication的AcquireRequestState和EndRequest事件处理。
4、AnonymousIdentificationModule提供匿名用户的标志:
是否启用匿名用户标志在配置文件的system.web配置元素的子元素anonymousIdentification中定义,还可以配置匿名标识的管理方式。
由于在AuthenticateRequest事件中将验证用户,获取用户名,所以这个Module注册了PostAuthenticateRequest的事件处理,当用户没有经过验证的时候,为用户分配一个唯一的匿名标识。
5、WindowsAuthenticationModule、FormsAuthenticationModule和PassportAuthenticationModule用来完成用户的验证工作。
它们通过配置文件中system.web的子元素authentication子元素定义,mode属性用来指定网站当前使用的验证方式,也就是哪一个Module将被用来完成验证工作。在启用验证的情况下,FormsAuthenticationModule和PassportAuthenticationModule将注册HttpApplication的AuthenticateRequest和EndRequest事件进行用户的验证处理。WindowsAuthenticationModule将注册AuthenticateRequest的事件处理。
6、RoleManagerModule、UrlAuthorizationModule、FileAuthorizationModule用来完成用户的授权管理:
授权管理的配置参数来自system.web的authorization子元素。UrlAuthorizationModule和FileAuthorizationModule注册了HttpApplication的AuthorizeRequest事件处理,
用来检查Url和文件的访问授权。RoleManagerModule在Url和文件访问授权检查通过之后,通过用户的标识和角色来完成用户的授权检查,
RoleManagerModule注册了HttpApplication的PostAuthenticateRequest和EndRequest事件处理。
17~19是DynamicModule,是MVC框架添加的。
移除指定的HttpModule
除了添加自己实现的HttpModule,我们还可以移除指定的HttpModule,只需要在web.config配置文件设设置,比如移除FormsAuthentication:
<system.webServer> <modules runAllManagedModulesForAllRequests="false"> ... <remove name="FormsAuthentication"/> ... </modules> ... </system.webServer>
再次展示所有的HttpModule,发现FormsAuthentication没有执行:

管道设计模型是一种很牛设计思想,使用灵活,我们可以对HttpModule随意控制,对某些请求移除不必要使用的HttpModule,添加自己的实现的HttpModule实现一些特定功能,甚至替换掉系统提供的某些HttpModule,自己再进行扩展。
HttpApplication事件的执行顺序
在Http请求流程中展示出了HttpApplication的所有事件,本次通过一个自定义的HttpModule来展示这些事件的执行顺序:
public class MyCustomModule : IHttpModule
{
/// <summary>
/// 您将需要在网站的 Web.config 文件中配置此模块
/// 并向 IIS 注册它,然后才能使用它。有关详细信息,
/// 请参阅以下链接: https://go.microsoft.com/?linkid=8101007
/// </summary>
#region IHttpModule Members
public void Dispose()
{
//此处放置清除代码。
}
public void Init(HttpApplication application)
{
application.AcquireRequestState += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "AcquireRequestState "));
application.AuthenticateRequest += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "AuthenticateRequest "));
application.AuthorizeRequest += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "AuthorizeRequest "));
application.BeginRequest += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "BeginRequest "));
application.Disposed += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "Disposed "));
application.EndRequest += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "EndRequest "));
application.Error += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "Error "));
application.LogRequest += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "LogRequest "));
application.MapRequestHandler += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "MapRequestHandler "));
application.PostAcquireRequestState += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "PostAcquireRequestState "));
application.PostAuthenticateRequest += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "PostAuthenticateRequest "));
application.PostAuthorizeRequest += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "PostAuthorizeRequest "));
application.PostLogRequest += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "PostLogRequest "));
application.PostMapRequestHandler += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "PostMapRequestHandler"));
application.PostReleaseRequestState += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "PostReleaseRequestState "));
application.PostRequestHandlerExecute += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "PostRequestHandlerExecute "));
application.PostResolveRequestCache += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "PostResolveRequestCache "));
application.PostUpdateRequestCache += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "PostUpdateRequestCache "));
application.PreRequestHandlerExecute += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "PreRequestHandlerExecute "));
application.PreSendRequestContent += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "PreSendRequestContent"));
application.PreSendRequestHeaders += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "PreSendRequestHeaders"));
application.ReleaseRequestState += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "ReleaseRequestState "));
application.RequestCompleted += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "RequestCompleted "));
application.ResolveRequestCache += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "ResolveRequestCache "));
application.UpdateRequestCache += (s, e) => application.Response.Write(string.Format("<h4 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h4><hr>", DateTime.Now.ToString(), "UpdateRequestCache "));
}
#endregion
public void OnLogRequest(Object source, EventArgs e)
{
//可以在此处放置自定义日志记录逻辑
}
}
在web.config中注册下,然后随便运行一个网友,效果如下:

Global中的事件
protected void GlobalModule_GlobalModuleEvent(object sender, EventArgs e)
{
Response.Write("来自Global中GlobalModule_GlobalModuleEvent的内容");
}
打开一个页面,效果如下:


浙公网安备 33010602011771号