在前面两篇文章中,我们仅从mvc源码的层次上解析了mvc执行的一些前期准备工作,那么这一篇文章我们将再次深入的探讨一个HTTP请求如何在IIS服务器以及.Net FrameWork环境中被解析并返回给客户端。在处理客户端请求的时候,这里会涉及到两个模块:IIS服务器和.net FrameWork环境。IIS服务器是运行在非托管的环境下,Asp.net管道则是在托管环境下的,两者之间通过ISAPI接口连通在一起。我在前面也讲过mvc其实走的也是asp.net管道,只不过是让UrlRoutingModule 实现了IHttpModule接口,并在Init中注册了PostResolveRequestCache事件,在该事件中进行了相应的扩展使得用于处理HTTP请求的处理器不再是WebForm模式下的页面类的处理器或是一般处理程序,而是MvcHandler处理器。同时后期不再是执行页面生命周期,打造控件树,而是通过视图引擎将处理后的结果返回给用户。
客户端的请求经过IIS的简单处理之后进入.net FrameWork环境,ISAPIRuntime会接管当前的HTTP请求,ISAPIRuntime首先会创建一个WorkRequest对象用于封装当前的HTTP请求,同时将WorkRequest对象传递给了Asp.net运行时HttpRuntime,到了这里意味着HTTP请求正式的进入到了Asp.net管道,在Asp.net事件管道里面涉及到两个十分重要的组件:HttpModule过滤器和HttpHanlder处理器。我们来一一介绍。
首先,我们来看HttpModule,其实不论是HttpModule还是HttpHandler都只是一个统称而已,分别代表某一类的过滤器和处理器。所有的HttpModule都必须实现IHttpModule接口,该接口定义如下:
|
它的强大之处在于它可以订阅任意的管道事件,有能力修改每个阶段的请求,将修改之后的请求交给HttpHandler处理器处理,因此这有可能影响到请求的处理。这主要体现在每一个HttpHandler都要实现了接口里面的Init(),因为web应用程序在启动的时候,会首先在配置文件中读取<modules></modules>节点,获取到相应的HttpModule处理器,并调用HttpModule里面的Init()方法实现订阅事件的目的。废话不多说,我们还是举一个URL重写的例子来讲解HttpModule的用法吧。我们自定义一个Httpmodule---MyUrlModule,让它实现IHttpModule接口,如下面代码所示:
public class MyUrlModule:IHttpModule { public void Init(HttpApplication context) { context.BeginRequest += context_BeginRequest; } void context_BeginRequest(object sender, EventArgs e) { HttpApplication app = sender as HttpApplication; app.Context.Response.ContentEncoding = System.Text.Encoding.UTF8; app.Context.Response.ContentType = "text/html"; app.Context.Response.Write("原始路径为:" + app.Context.Request.Url.AbsolutePath); //将原始路径:NewsPage.aspx/id/4转换为NewsPage.aspx?Id=4--即URL重写的本质://获得原始的路径---请求报文传递过来的URL string sourcePath = app.Context.Request.Url.AbsolutePath+"/id/4"; //将URL进行分割 string[] urls = sourcePath.Split('/'); //获得类类名跟后缀名 string strClassName = urls[1]; string[] strClassAndExtend = strClassName.Split('.'); //获得类名 string className = strClassAndExtend[0];//获得后缀名 string extend = strClassAndExtend[1]; //获得传递过来的参数 string strParas = urls[2];//获得传递过来的参数值 string strParasValue = urls[3];//将伪装的静态页转换为动态页面 app.Context.RewritePath(className+"."+"aspx"+ "?" + strParas + "=" + strParasValue); } |
同时HttpModule在Web.Config中配置文件的设置如下:
<system.webServer> <modules> <add name="url" type="URLModule.MyUrlModule"/> </modules> </system.webServer> |
这样在web程序初始化的时候或是处理请求的时候都会调用HttpModule的Init事件,因此在这里面我们需要特别注意一点:Asp.net中会为每一个到达管道的请求(不包括只是请求.js,.css或者img等资源)都创建一个HttpApplication对象,当HttpApplication对象初始化的时候都会加载Web.Config中注册的HttpModule节点。而Asp.net中创建的HttpApplication对象并不是唯一的,也就是意味着HttpModule中的Init方法可能会被调用多次,因此对于类似初始化的操作这里还是不适合的。还有一点我们也需要注意,我们要为Httpmodule订阅合适的管道事件。我之前在项目开发的时候就遇到过这样的一个问题:利用Seesion(现在想想用session确实不怎么好)对用户的是否登录进行判断,当时也是订阅BeginRequest事件,但是怎么操作都无法显示预期的效果。后来才发现在PostAcquireRequestState与PostRequestHandlerExecute事件中才开始加载了页面类的Session对象,自然就无法在BeginRequest事件中做用户登录的判断了。
我们再来看看HttpHandler处理器,所有的HttpHandler都是实现了IHttpHandler接口,HttpHandler是处理所有请求的核心对象。绝大多数的的请求都在PostMapRequestHandler事件中被映射到一个HttpHandler对象, 然后在PreRequestHandlerExecute事件中执行处理过程,因此也常把这类对象称为处理程序,像在WebForm模式下的一般处理程序.ashx和Page就是一个处理器。我个人认为一般处理程序.ashx文件就像一个纯正的处理器,在WebForm编程中,我使用一般处理程序的概率还是很高的,简洁高效且在js或是Ajax中可以直接指定要哪一个一般处理器来处理我们的请求。当然了我们依然也可以创建我们自己的Httphandler处理器,将某一类请求交给我们自己定义的Httphandler处理,这样的话我们就得在Web.Config中注册我们处理器。
<httpHandlers> <add path="/MyTestHttpModule.axd" verb="*" validate="false" type="URL.MyTestHttpModule"/> </httpHandlers> |
那么在mvc中HttpHandler处理器就是MvcHandler,并由它处理请求实现了mvc。
对HttpHandler这一块理解的还不是很透彻,写下愚见,望共勉~~~~