HttpModule工作方式:订阅管线事件,并在事件处理中执行所需要的相关操作.
它的无限强大处理能力正是来源于它可以订阅管线事件,因此,它有能力在许多阶段修改请求,这些修改的请求可能会影响最终的处理结果(比如完全不通过HttpHandler直接处理请求)
HttpModule加载方式:
Asp.net会为每一个请求分配一个HttpApplication对象,在每一个HttpApplication对象初 始化的过程中,它会加载所有在Web.Config上注册的HttpModule,由于Asp.net并不是只创建一个HttpApplication对象,而是创建多个HttpApplication对象,因此每个HttpModule的Init()事件可能会被多次调用,许多人喜欢在这做类初始化操作,那么需要注意修改静态成员变量时的线程安全情问题,所以,如果需要执行初始化操作,最好还是在Global的Application_Start事件中处理.
HttpModule性能问题
对于每个Asp.net请求,每个HttpModule会在它所订阅的事件中,会执行一些操作逻辑,这些操作逻辑对请求可能是无用的,因此会浪废一些资源和白白执行一些无意义的代码.
以下是微软在我们项目中加载的HttpModule
protected void Page_Load(objectsender,EventArgs e)
{
HttpApplication app = HttpContext.Current.ApplicationInstance;
StringBuilder sb = new StringBuilder();
foreach( string module in app.Modules.AllKeys )
sb.AppendFormat(module).Append("<br />");
Response.Write(sb.ToString());} 总共有14个,因此可以将一些不需要的移除
HttpModule常见用法
1 Url重写
通常情况下,将一个Url:/Product/12 重写为/Product.aspx/?id=12,这样的目的是让地址更友好
实现原理:订阅管线的PostAuthorizeRequest事件,检查Url是不是期望修改的模式,如果是,调用Context.RewritePath()方法完成URL重写操作.在管线的后续处理过程中,最终会使用新的URL映射到的个HttpHandler上,注意:选择的事件只需要在第10个事件之前就可以,因为在第10个事件之前重写URL,才能保证将请求映射到合理的处理器上执行。(代码详见FishLi)
2Url路由
通过HttpModule实现Url路由是MVC中常用的模式
实现原理:订阅管线中的PostResolveRequestCache事件,检查Url是不是期望的路由模式,如果是,则根据请求中所包含的信息找到一个合适的处理器,并临时保存这个处理器,重写Url到一个Asp.net可以映射的地址,在管线的PostMapRequestHandler事件中,检查前面有没有临时保存处理器,如果有,则重新给Context.Handler赋值,并重写Url原始地址。
public class MyServiceUrlRoutingModule : IHttpModule { private static readonly object s_dataKey = new object(); public void Init(HttpApplication app) { app.PostResolveRequestCache += new EventHandler(app_PostResolveRequestCache); app.PostMapRequestHandler += new EventHandler(app_PostMapRequestHandler); } private void app_PostResolveRequestCache(object sender, EventArgs e) { HttpApplication app = (HttpApplication)sender; // 获取合适的处理器,注意这是与URL重写的根本差别。 // 即:根据当前请求【主动】寻找一个处理器,而不是使用RewritePath让Asp.net替我们去找。 MyServiceHandler handler = GetHandler(app.Context); if( handler == null ) return; // 临时保存前面获取到的处理器,这个值将在PostMapRequestHandler事件中再取出来。 app.Context.Items[s_dataKey] = handler; // 进入正常的MapRequestHandler事件,随便映射到一个处理器就行了。 app.Context.RewritePath("~/MyServiceUrlRoutingModule.axd"); } private void app_PostMapRequestHandler(object sender, EventArgs e) { HttpApplication app = (HttpApplication)sender; // 取出在PostResolveRequestCache事件中获得的处理器 MyServiceHandler handler = (MyServiceHandler)app.Context.Items[s_dataKey]; if( handler != null ) { // 还原URL请求地址。注意这里和URL重写的差别。 app.Context.RewritePath(app.Request.RawUrl); // 还原根据GetHandler(app.Context)调用得到的处理器。 // 因为此时app.Context.Handler是由"~/MyServiceUrlRoutingModule.axd"映射得到的。 app.Context.Handler = handler; } }
注意:在MyServiceUrlRoutingModule中,我将请求【路由】到一个MyServiceUrlRoutingModule的实例,而不是让Asp.net根据URL来替我选择