ASP.NET MVC生命周期介紹
asp.net應(yīng)用程序管道處理用戶請求時特別強調(diào)"時機",對asp.net生命周期的了解多少直接影響我們寫頁面和控件的效率。對于asp.net mvc,我對它的生命周期興趣很濃,于是對ASP.NET MVC生命周期提出兩個問題:
一個HTTP請求從IIS移交到asp.net運行時,asp.net mvc是在什么時機獲得了控制權(quán)并對請求進行處理呢?處理過程又是怎樣的?
以IIS7中asp.net生命周期為例,上圖是來自MSDN的一張HTTP請求處理過程發(fā)生事件的簡圖,后面我列出了一個完整的事件列表。既然asp.net mvc還是以asp.net運行時為基礎(chǔ)那么它必然要在asp.net應(yīng)用程序的生命周期中對請求進行截獲。第一反應(yīng)當(dāng)然是去web.config里面去翻翻,我們可以看到UrlRoutingModule的配置節(jié):
< add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
下面要做的就順理成章了,用Reflector打開這個程序集,可以看到以下代碼:
- protected virtual void Init(HttpApplication application)
- {
- application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
- application.PostMapRequestHandler += new EventHandler(this.OnApplicationPostMapRequestHandler);
- }
看到這里我們的第一個問題實際上已經(jīng)有了答案:時機是在PostResolveRequestCache和PostMapRequestHandler.
我們使用VS2008中asp.net mvc模板創(chuàng)建一個Demo完成后續(xù)的討論,當(dāng)我們訪問/Home的時候發(fā)生了什么呢?
1、Request 請求到來
2、IIS 根據(jù)請求特征將處理權(quán)移交給 asp.net
3、UrlRoutingModule將當(dāng)前請求在 Route Table中進行匹配
4、UrlRoutingModule在RouteCollection中查找Request匹配的RouteHandler,默認是mvcRouteHandler mvcRouteHandler 創(chuàng)建 mvcHandler實例.
5、mvcHandler執(zhí)行 ProcessRequest.
6、mvcHandler 使用 IControllerFactory 獲得實現(xiàn)了IController接口的實例,找到對應(yīng)的HomeController
7、根據(jù)Request觸發(fā)HomeController的Index方法
8、Index將執(zhí)行結(jié)果存放在ViewData
9、HomeController的Index方法返回 ActionResult
10、Views/Home/Index.aspx將 ViewData呈現(xiàn)在頁面上
11、Index.aspx執(zhí)行ProcessRequest方法
12、Index.aspx執(zhí)行Render方法 輸出到客戶端
通過閱讀asp.net mvc的源碼,我們可以得到更為詳細的處理過程,我盡可能的忽略掉枝節(jié),強調(diào)請求處理的流程.我們從Global.asax.cs文件切入,下面是一段樣例代碼,這里初始化了路由表,請?zhí)貏e特別注意注釋部分:
- public class mvcApplication : System.Web.HttpApplication
- {
- public static void RegisterRoutes(RouteCollection routes)
- {
- routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
- //The controller route value is a special value that the System.Web.mvc.mvcHandler class uses to call into the IControllerFactory interface.
- //The basic route handler is an instance of IRouteHandler named mvcRouteHandler.
- //We have complete control and could provide our own implementation of IRouteHandler if we wished.
- routes.MapRoute(
- "Default", // Route name
- "{controller}/{action}/{id}", // URL with parameters
- new { controller = "Home", action = "Index", id = "" } // Parameter defaults
- );
- }
- protected void Application_Start()
- {
- RegisterRoutes(RouteTable.Routes);
- }
UrlRoutingMoudule在PostResolveRequestCache階段從RouteCollection中獲取當(dāng)前請求的RouteData.RouteData包含了一個請求處理對應(yīng)的Controller和Action,RouteData這個作用貫穿請求的處理過程.RouteData中提取RouteHandler,這里默認是mvcRouteHandler,mvcRouteHandler獲取HttpHandler,這里默認的是mvcHandler.
- PostResolveRequestCache
- public virtual void PostResolveRequestCache(HttpContextBase context)
- {
- RouteData routeData = this.RouteCollection.GetRouteData(context);
- if (routeData != null)
- {
- IRouteHandler routeHandler = routeData.RouteHandler;
- 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);
- if (httpHandler == null)
- {
- throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, RoutingResources.UrlRoutingModule_NoHttpHandler, new object[] { routeHandler.GetType() }));
- }
- RequestData data2 = new RequestData();
- data2.OriginalPath = context.Request.Path;
- data2.HttpHandler = httpHandler;
- context.Items[_requestDataKey] = data2;
- context.RewritePath("~/UrlRouting.axd");
- }
- }
- }
mvcHandler.ProcessRequest()中首先使用HttpContextWrapper對HttpContext進行封裝,封裝的目的是為了解耦以獲得可測試性.然后從RequestContext.RouteData中提取Controller名稱.
ControllerBuilder.GetControllerFactory --> ControllerFactory.CreateController --> IController.Execute
ControllerBase實現(xiàn)了IController接口,在Initialize時將RequestContext封裝成為ControllerContext,Controller繼承自ControllerBase并實現(xiàn)抽象方法ExecuteCore()
在ExecuteCore中,Controller首先從RouteData中獲得ActionName,然后執(zhí)行ActionInvoker.InvokeAction.
在ActionInvoker中我們可以看到各種Filter,這是一種AOP實踐:在Action方法執(zhí)行的前后執(zhí)行若干方法.這里有四種Filter:ActionFilters,ResultFilters,AuthorizationFilters,ExceptionFilters.這四種Filter并不是封閉的,都有對應(yīng)的接口,這四個只是默認實現(xiàn).Filter的執(zhí)行順序是:AuthorizationFilter--->Action Filter.OnActionExecuting--->Action Method--->ActionFilter.OnActionExecuted.InvokeActionMethodWithFilters返回的結(jié)果是ActionExecutedContext,接下來將Controller執(zhí)行OnResultExecuting 方法.ActionResult執(zhí)行的結(jié)果可以是ViewResult,JsonResult,RedirectResult,ContentResult,或者是自定義的Result類型.
如果返回的類型是ViewResult,我們先看一下ViewReuslt的繼承關(guān)系:ViewResult-->ViewResultBase-->ActionResult,ViewResult包含兩個屬性View和ViewEngineCollection,實際上是包含了兩個接口的實現(xiàn):IViewEngine定義了怎么定位View/Partial View.IView定義了如何RenderView.默認的實現(xiàn)時WebFormView和WebFormViewEngine.
Filter OnResultExecuted 最后一步了,可以這里捕獲異常.上面我們說過還有ExceptionFilters,如果前面過程中的異常沒有被捕獲那么最終都會到冒泡到ExceptionFilters.
RouteData中獲得ActionName
ActionInvoker.InvokeAction
通過ControllerContext獲取ControllerDescriptor
FindAction-獲取ActionDescriptor
GetFilters
ModelBinder把Request中的數(shù)據(jù)轉(zhuǎn)換成Action方法需要的參數(shù)
AuthorizationFilter
Action Filter.OnActionExecuting
Action
ActionFilter.OnActionExecuted
ResultFilter.OnResultExecuting
ActionResult Execution
ResultFilter.OnResultExecuted
WebFormViewEngine.CreateView
WebFormView.Render
ResultFilter.OnExecuted
控制權(quán)歸還到HttpApplication完成后續(xù)的asp.net mvc生命周期。
【編輯推薦】
- ASP.NET數(shù)組基礎(chǔ):聲明,表示及范例
- ASP.NET編程中的十大技巧(二)
- ASP.NET編程中的十大技巧(一)
- ASP.NET MVC教程(一):準備工作
- ASP.NET MVC Beta的部署問題