1、概要
當我們新建一個MVC項目時,打開他的Web.Config文件可以發現
<httpModules> <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> 我們知道ScriptModule 類就是管理用於 ASP.NET 中 AJAX 功能的 HTTP 模塊,在此我們不做介紹 <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> //這個UrlRoutingModule類才是重點 </httpModules>
這個HttpModule,攔截所有請求,對請求進行處理,最終創建和執行合適的處理請求的HttpHandler(MVC3之后,這個UrlRoutingModule集成到MVC程序集中了)。
當客戶端在本地瀏覽器上輸入網址來請求咱們的一個MVC程序時,服務端接收到請求.....此處省略N個字(和asp.net處理一樣).....
HttpApplication的事件注冊,即將 UrlRoutingModule 注冊到HttpApplication的事件中
public class UrlRoutingModule : IHttpModule { protected virtual void Init(HttpApplication application) { //開始只是把要執行的具體方法注冊到事件中,等待事件被觸發時,在執行已被注冊的方法。 application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache); application.PostMapRequestHandler += new EventHandler(this.OnApplicationPostMapRequestHandler); } }
注冊完事件之后,那么就要開始執行HttpApplication事件。
1、執行Global.asax文件中Application_Start方法。
即:在此處將一個自己定義的路由規則注冊到路由集合中。這個路由集合可以由RouteTable.Routes獲得。
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterRoutes(RouteTable.Routes); } public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Default", // 路由名稱 "{controller}/{action}/{id}", // 帶有參數的 URL new { controller = "Home", action = "Index", id = UrlParameter.Optional } // 參數默認值 ); //在路由表里添加一條路由規則 }
2、依次執行HttpApplication的事件。
BeginRequest
AuthenticateRequest
PostAuthenticateRequest
AuthorizeRequest
PostAuthorizeRequest
ResolveRequestCache
PostResolveRequestCache 在UrlRoutingModule類中,在此事件中注冊了一個執行方法,即:OnApplicationPostResolveRequestCache PostMapRequestHandler OnApplicationPostMapRequestHandler
AcquireRequestState PostAcquireRequestState PreRequesHandlerExecute PostRequeshandlerExecute ReleaseRequesState PostReleaseRequestState UpdateRequestCache PostUpdateRequestCach LogRequest PostLogRequest EndRequest
OnApplicationPostResolveRequestCache方法
private void OnApplicationPostResolveRequestCache(object sender, EventArgs e) { HttpContextBase context = new HttpContextWrapper(((HttpApplication) sender).Context); this.PostResolveRequestCache(context); } //這里用HttpContextWrapper類包裝當前的HttpContext,實質上也是一個請求的上下文。他可以使用諸如Typemock Isolator或Rhino Mocks的Mock對象框進行模擬變得更簡單。 //並把這個包裝之后的上下文作為PostResolveRequestCache的參數 ------------------------------------------------------------------------------------------------------------------ public virtual void PostResolveRequestCache(HttpContextBase context) { RouteData routeData = this.RouteCollection.GetRouteData(context); //GetRouteData方法內部遍歷路由集合中的每個路由對象去和上下文中指定的請求URL去匹配。如成功,就返回當前的路由對象RouteData,如不成功,返回null //this.RouteCollection就是RouteTable.Routes,即:路由集合。 if (routeData != null)----這里便是判斷是否匹配成功 { IRouteHandler routeHandler = routeData.RouteHandler;----//獲取一個處理當前匹配成功的路由的對象
//這個routeHandler其實就是一個MvcRouteHandle類
----因為在第一句中執行的GetRouteData方法中,為RouteData的RouteHandler屬性賦值為MvcRouteHandler if (routeHandler == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, RoutingResources.UrlRoutingModule_NoRouteHandler, new object[0])); } if (!(routeHandler is StopRoutingHandler)) { RequestContext requestContext = new RequestContext(context, routeData);//把當前的請求的信息和與當前請求匹配成功的路由信息再包裝起來。 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); //根據包裝后的請求信息,最終得到一個MvcHandler
---MvcRouteHandler中只有一個方法,GetHttpHandler方法,返回MvcHandler,Mvc中處理請求的類。
---PageRouteHandler中也只有一個方法,GetHttpHandler方法,返回的是Page,asp.net中處理請求的類。

public class MvcRouteHandler : IRouteHandler { protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) { return new MvcHandler(requestContext); } IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext) { return this.GetHttpHandler(requestContext); } }
if (httpHandler == null)
{ throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, RoutingResources.UrlRoutingModule_NoHttpHandler, new object[] { routeHandler.GetType() })); } //RequestData類是UrlRoutingModule類中嵌套的一個私有類,把處理請求的類和當前請求的虛擬路徑 RequestData data2 = new RequestData { OriginalPath = context.Request.Path, HttpHandler = httpHandler }; context.Items[_requestDataKey] = data2; //把封裝的處理類MvcHandler和請求的虛擬路徑,賦值到 HttpContextWrapper類中。(這樣在用到處理類時,就需要實例化,直接取值即可) //HttpContextWrapper類包裝當前的HttpContext,實質上也是一個請求的上下文。 context.RewritePath("~/UrlRouting.axd");
context.RemapHandler(httpHandler);//博客是里這里是這么一句,但是我反編譯沒找到,可能是版本的問題吧!
//將MvcHandler 實例 映射到管線中(通常我們是利用web.config 進行配置的,但是MvcHandler 沒有默認無參構造函數,所以直接通過向其傳遞一個實例進行映射)
} } }
OnApplicationPostMapRequestHandler方法
該方法做的事情很簡單,就是重寫下請求路徑,讓輸出的路徑和輸入的路徑相同,在這里用來記憶輸入路徑的是context.Items[],從上下兩段代碼中可以看到.
這個事件負責根據文件擴展名映射到具體的httphandle處理類,而MVC的URL信息沒有具體的文件后綴名 為了使處理模塊能夠在iis7中實現路由,則采取了這么一種簡單的解決辦法。先把路徑指向~/UrlRouting.axd,在此事件中會設置一個UrlRouting.axd類型的Handler避免報錯,並在下一步事件中替換掉此處的Handler再把~/UrlRouting.axd這個路徑給改回來。

private void OnApplicationPostMapRequestHandler(object sender, EventArgs e) { HttpContextBase context = new HttpContextWrapper(((HttpApplication) sender).Context); this.PostMapRequestHandler(context); } public virtual void PostMapRequestHandler(HttpContextBase context) { RequestData data = (RequestData) context.Items[_requestDataKey]; if (data != null) { context.RewritePath(data.OriginalPath); context.Handler = data.HttpHandler; } }
上文中得到了一個MvcHandler類實例,MvcHandler繼承實現了IHttpAsyncHandler, IHttpHandler, IRequiresSessionState三個接口。而這三個接口如果都實現了,在MVC框架下是不是任何http請求就可以通吃了嗎?從MSDN我們得知,事實不是這樣的:注意,即使 MvcHandler 實現 IHttpHandler,也不能將其映射為處理程序(例如.mvc 文件擴展名),因為該類不支持無參數構造函數。 (它唯一的構造函數需要一個 RequestContext 對象)
但是,還好,我們還有MvcHttpHandler。
如你所知,MvcHttpHandler可以“彌補”MvcHandler的不足,為什么這樣說呢?因為MvcHandler沒有無參的構造函數,因此即使MvcHandler實現了 IHttpHandler接口,在IIS中也不能將其映射為某類文件擴展名的處理程序,而MvcHttpHandler就提供了不通過路由模塊的情況下直接處理映射的處理程序。
-
MvcHttpHandler.使用此處理程序可便於實現直接處理程序映射(不通過路由模塊)。如果要將文件擴展名(如 .mvc)直接映射到一個 MVC 處理程序,此處理程序將非常有用。在內部,MvcHttpHandler 將執行 ASP.NET 路由通常執行(通過 MvcRouteHandler 和 MvcHandler)的任務。但是,它是作為處理程序而不是作為模塊來執行這些任務的。對所有請求啟用 UrlRoutingModule 時,通常不使用此處理程序。
-
MvcHandler.此處理程序負責啟動 MVC 應用程序的 ASP.NET 管道。它從 MVC 控制器工廠接收 Controller 實例;此控制器處理請求的進一步處理。請注意,即使 MvcHandler 實現了 IHttpHandler,它也不能映射為處理程序(例如,針對 .mvc 文件擴展名),因為該類不支持無參數構造函數(而處理程序要求是無參數構造函數)。(其唯一的構造函數需要 RequestContext 對象。)
3、HttpApplication事件繼續執行
BeginRequest
AuthenticateRequest
PostAuthenticateRequest
AuthorizeRequest
PostAuthorizeRequest
ResolveRequestCache
PostResolveRequestCache
PostMapRequestHandler
AcquireRequestState
PostAcquireRequestState
PreRequesHandlerExecute PostRequeshandlerExecute
ReleaseRequesState PostReleaseRequestState UpdateRequestCache PostUpdateRequestCach LogRequest PostLogRequest EndRequest
在11-12個事件的時候拿到第7個事件的時候創建的MVCHandler對象執行他的ProcessRequest方法。
public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState { protected virtual void ProcessRequest(HttpContext httpContext) { //使用HttpContextWrapper對HttpContext進行封裝,封裝的目的是為了解耦以獲得可測試性.然后從RequestContext.RouteData中提取Controller名稱. HttpContextBase httpContext2 = new HttpContextWrapper(httpContext); this.ProcessRequest(httpContext2); } protected internal virtual void ProcessRequest(HttpContextBase httpContext) { IController controller; IControllerFactory controllerFactory; this.ProcessRequestInit(httpContext, out controller, out controllerFactory); //獲取到Controler實例 ----下節詳細介紹 try { controller.Execute(this.RequestContext); //當前Controler對象的Action的創建與執行 ----下節詳細介紹
執行包括:加載TempData, 創建及執行Action,處理Action返回的ActionResult ,保存TempData數據。 } finally { controllerFactory.ReleaseController(controller); //釋放當前Controler對象 } } }
流程如下圖,MvcHandler實例來處理請求,他做為處理的主干,當完成之后,釋放當前的Controler實例,繼續執行HttpApplication事件
此圖摘自:http://www.cnblogs.com/terrysun/archive/2010/03/19/1690052.html