对于每个运行中的应用程序,ASP.NET都维护着一个HttpApplication对象的池,特定的请求会逐一由池中的实例处理。这些对象基于定义在global.asax的类创建,如果未定义global.asax,则基于HttpApplication类创建。负责处理请求的HttpApplication对象的最终目标是获取HTTP处理程序。
在获取目标HTTP处理程序前,HttpApplication对象要使请求流经HTTP模块的管道。HTTP模块是实现IHttpModule接口的.NET Framework类,用于筛选请求的中原始数据。HTTP模块会基于每个应用程序的web.config文件进行配置,但所有ASP.NET应用程序都会继承全局的web.config文件中配置的许多系统HTTP模块。
一般来讲,HTTP模块可以对请求进行预处理和后处理,它会监听和处理系统事件和其他模块引发的事件。ASP.NET固有的可配置特性使我们能够编写和注册自定义的HTTP模块,并将其插入到ASP.NET运行时管道中来处理系统和自定义事件。
IHttpModule接口
IHttpModule接口定义了两个方法:Init和Dispose。Init方法用于对模块进行初始化,为处理请求做好准备。同时,我们还可以通过它来订阅所需的事件,以便得到通知。Dispose方法用于释放当前模块占用的资源(除内存外的所有资源)。我们一般在Dispose方法中关闭数据库连接或文件句柄。这两个方法的签名如下:
void Init(HttpApplication app); void Dispose();
下表列出了HTTP模块能够监听和处理的事件:
自定义的HTTP模块
让我们编写一个相对简单的自定义模块Marker,它对当前应用程序处理的所有页面添加页眉和页脚。该类的代码如下:
using System; using System.Web; using System.IO;
namespace Core35.Components { public class MarkerModule : IHttpModule { #region Constants const string PageHeaderText = "<table width=100% style='font-family:verdana;font-size:8pt;' bgcolor=cyan><tr><td align=center>You must register to continue enjoy this Web site! Click <b><a href=http://www.contoso.com>here</a>.</b></td></tr></table>"; const string PageFooterText = "<table width=100% style='font-family:verdana;font-size:8pt;' bgcolor=cyan><tr><td align=center>Courtesy of <b>Programming Microsoft ASP.NET 3.5</b></td></tr></table>"; #endregion
#region IHttpModule public void Init(HttpApplication app) { // Register for pipeline events app.BeginRequest += new EventHandler(OnBeginRequest); app.EndRequest += new EventHandler(OnEndRequest); }
public void Dispose() { // Nothing to do here } #endregion
#region Helpers public void OnBeginRequest(object sender, EventArgs e) { HttpApplication app = (HttpApplication) sender; HttpContext ctx = app.Context;
// More code here if (!ShouldHook(ctx)) return;
// Add custom header to the HTTP response ctx.Response.AppendHeader("Author", "DinoE"); ctx.Response.Write(PageHeaderText); }
public void OnEndRequest(object sender, EventArgs e) { HttpApplication app = (HttpApplication)sender; HttpContext ctx = app.Context;
// More code here if (!ShouldHook(ctx)) return;
// Append some custom text ctx.Response.Write(PageFooterText); }
public bool ShouldHook(HttpContext ctx) { return ctx.Request.Path.EndsWith("testmarker.aspx", StringComparison.OrdinalIgnoreCase); } #endregion } }
OnBeginRequest添加了一个普通的页面眉和一个自定义的HTTP标头。OnEndRequest只添加页脚。
使用配置文件来注册该模块
我们需要在web.config注册HTTP模块,以使模块生效:
<system.web> <httpModules> <add name="Marker" type="Core35.Components.MarkerModule,Core35Lib" /> </httpModules> </system.web>
模块生效的顺序取决于它们在配置列表中的物理顺序。我们可以移除某个系统模块,并替换成具有类似功能的自定义模块。在这种情况下,可以在应用程序的web.config文件中使用<remove>节点来删除默认的模块,通过<add>来插入自定义的模块。如果希望为应用程序重新定义所有HTTP模块的顺序,可使用<clear>节点,然后按意愿重新注册。
HTTP模块只在应用程序启动时加载和初始化一次。与HTTP处理程序不同,模块会作用于所有请求。
其他HTTP模块的访问
上例演示了如何订阅管道的事件(即由HttpApplication对象引发的事件)。那么,如果想订阅其他模块引发的事件,如何捕获呢?HttpApplication对象提供了一个名为Modubles的属性,用于获取当前应用程序中模块的集合。
Modules的类型为HttpModuleCollection,包含应用程序中各模块的名称。该集合类继承于抽象类NameObjectCollectionBase,这个抽象类代表由字符串/对象对组成的集合。字符串指示模块的公共名称,对象是模块的实例。如要访问处理会话状态的模块:
SessionStateModule sess = app.Modules["Session"]; sess.Start += new EventHandler(OnSessionStart);
我们还可在global.asac文件中使用ModuleName_EventNamer的事件处理程序命名范式来处理HTTP模块引发的事件。
|
请发表评论