閱讀目錄:
- 1.開篇介紹
- 2.ASP.NETMVC Controller 控制器的入口(Controller的執行流程)
- 3.ASP.NETMVC Controller 控制器的入口(Controller的繼承體系)
- 4.ASP.NETMVC IController Factory 控制器工廠(Controller的創建)
1】開篇介紹
經過前一篇文章.NET/ASP.NET Routing路由(深入解析路由系統架構原理) 的講解,我們對ASP.NETRouting路由系統的整個運行機制有了一個基本的了解;當我們能清楚的知道Url是如何被解析成RouteData對象時,下面就是這些路由數據是如何被后面的應用框架所使用的,而通往應用框架的入口是MvcRouteHandler對象;
這篇文章將繼續講解通過路由后的ASP.NETMVC Controller控制器是如何被加載、激活並且執行的;跟控制器相關的一套對象模型是被MvcHandler對象作為源頭調用起來的,也就是說,當我們穿過UrlRoutingModule對象后,並且成功的獲取到應用框架配置的路由數據后,下面將進入IHttpHandler接口,而這個接口真是我們初始化RouteData對象時設定的應用框架入口,ASP.NETMVC所使用的是MvcHandler對象;
MvcRouteHandler對象是UrlRoutingModule和MvcHandler對象的連接器,只有MvcRouteHandler對象能成功執行后,方能進入到MvcHandler對象中,后續的一切運轉才能順利執行;
2】ASP.NETMVC Controller 控制器的入口(Controller的執行流程)
在系統剛啟動的時候,也就是在Global.asax.cs文件里面我們配置了Http客戶端請求服務器的Url模板;在路由解析模塊(UrlRoutingModule)里面,它將通過字符串級別的操作,解析出我們Url模板中的{Controller}/{Action}等的占位符變量;所以這個時候Controller的概念對我們來說還只是一個字符串而已,而到了目前的這個Controller控制器解析的位置其實已經和路由基本沒關系了,因為我們穿過了路由模塊到達了Controller解析的環節;Controller解析已經屬於ASP.NETMVC應用框架的范圍,我們可以簡單的將路由解析(UrlRoutingModule)的過程視為將請求的Url(含有數據的Url)與我們配置的Url模板進行模式匹配的過程,得出匹配后的Url數據(RouteData),然后將Url數據並且連同當前請求上下文一起封裝成RequestContext對象(RouteData、HttpContextBase)傳入到Controller解析的環節,也就是MvcHandler中,作為MvcHandler構造函數的參數;
當MvcHandler接管控制權之后它需要准備好對Controller的解析和執行,但是Controller並發一個簡單的對象,它有一個復雜的繼承體系和使用方式,原因在於它需要協調多方面的工作所以變的有很復雜;
根據MVC的架構模式理論便知道Controller是協調Model與View的中間紐帶,它既要管理好Model的執行,也要管理好View的呈現;而原本MVC的架構模式提出的背景是在WinFrom的情況下,也就是傳統C/S結構的系統;WinFrom結構的系統有一個好處就是它的執行很方便,從View的展現收集數據到Controller的調度執行Model會容易完成,但是ASP.NETMVC是建立在ASP.NET WEB背景之下的MVC模式框架,所以這個時候對Controller的激活會變的相當麻煩,因為在傳輸過程中Controller已經是字符串形式,如果是在C/S結構中那么Controller對於每次處理一樣的View不會每次都進行激活;既然每次都需要激活就需要進行緩存策略,緩存策略只是Controller中的一個關鍵點,需要明白的是Controller的確需要做很多事情;
圖1:
根據上圖的執行順序,能看出Controller控制器扮演着一個很重要的角色,所有的執行、返回值、視圖呈現均需要通過它來管理調度;當然本章的重點是搞清楚此圖中的第一環節,Controller是如何被加載激活的,這里面將涉及到眾多的輔助對象模型,比如:ControllerFactory控制器工廠,而控制器工廠又將借助ControllerTypeCache來緩存Controller對象,而ConrollerTypeCache又將借助TypeCacheSerializer來對Controller緩存文件的序列化;
3.ASP.NETMVC Controller 控制器的入口(Controller的繼承體系)
Controller控制器既然扮演着重要的角色,那么它就不會是一個簡單的對象結構,它有着一個復雜的繼承體系和對象模型支撐它來完成這些艱巨的任務;Controller要想能夠運行起來,就需要搞清楚它有哪些執行入口,而需要知道它有哪些執行入口我們就需要搞清楚它的繼承體系;入口的最高層抽象在哪一層,這樣我們才能舉一反三的擴展Controller的眾多重要的功能;
首先我們了解到Controller的頂層抽象是IController接口,然后接着是ControllerBase抽象類實現了這個接口,而作為頂層抽象的實現ControllerBase完成了從IController接口繼承下來的方法;
1 public interface IController { 2 void Execute(RequestContext requestContext); 3 }
通過該代碼段可以看出,Controller的執行需要一個RequestContext對象,而這個對象真是UrlRoutingModule環節所完成的結果,RequestContext對象內部封裝了在Request階段所獲得的請求數據,里面包括了跟Http相關的請求上下文(HttpContextBase),最重要的是路由數據對象(RouteData);而控制器的執行必須需要RouteData中的有關Controller數據對象,也就是從請求Url中通過模式匹配出來的{Controller}部分的字符串;
ControllerBase定義了Controller使用到的部分公共屬性,比如:用來保存臨時數據的TempData,用來返回到View中的Model數據對象ViewBag、ViewData;並且初始化了ControllerContext對象,用來作為后續Controller使用的數據容器和操作上下文;
1 protected virtual void Initialize(RequestContext requestContext) { 2 ControllerContext = new ControllerContext(requestContext, this); 3 }
在ControllerBase中將對IController.Execute(RequestContext requestContext)方法調用轉到了protected abstract void ExecuteCore()方法中;這是一個典型的模板方法模式,下面的繼承類Controller,只需要接着protected abstract void ExecuteCore()方法就能和ControllerBase銜接上;
public abstract class Controller : ControllerBase
Controller類繼承自ControllerBase,而Controller的任務只需要完成ExecuteCore()方法;
1 protected override void ExecuteCore() { 2 3 PossiblyLoadTempData(); 4 try { 5 string actionName = RouteData.GetRequiredString("action"); 6 if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) { 7 HandleUnknownAction(actionName); 8 } 9 } 10 finally { 11 PossiblySaveTempData(); 12 } 13 }
Controller.ExecuteCore()的代碼將從RouteData中獲取執行action的名稱,然后通過一個ActionInvoke的組件進行Action的調用,當Action被執行的時候將進入到我們繼承的Controller,如:HomeController:Controller中,在我們自定的Controller中的方法都將被視為Action的匹配目標之一;
圖2:
根據上圖的指示,ControllerBase首先是實現IController接口,完成了對Execute(RequestContext requestContext)方法的實現,然后Controller繼承ControllerBase類,重寫了模板方法ExecuteCore()方法,然后我們自定義的HomeController其實是Action的容器,當Controller的ExecuteCore()方法執行時將通過ActionInvoke類進行對HomeController中的方法調用;
4.ASP.NETMVC IController Factory 控制器工廠(Controller的創建)
當清楚了Controller的繼承體系之后,下面回到MvcHandler調用的環節;MvcHandler繼承自IHttpHandler接口 ,表示它將是ASP.NET真正執行請求處理的地方;在MvcHandler處理請求的方法中ProcessRequest(HttpContextBase httpContext),將通過IControllerFactory接口創建IController接口;
IControllerFactory接口是控制器工廠接口,專門用來實現創建IController對象工廠類,在ASP.NETMVC內部有一個實現了IControllerFactory接口的默認工廠類DefaultControllerFactory,ASP.NETMVC內部是用這個類來創建IController對象的;
1 factory = ControllerBuilder.GetControllerFactory();
獲取IDefaultControllerFactory接口需要通過ControllerBuilder對象,ControllerBuilder類是專門用來管理IControllerFactory對象的,同時ControllerBuilder也是應用編程接口,讓自定義IControllerFactory對象成為可能;
創建IController需要我們傳入RequestContext對象和ControllerName控制器名稱;
1 // Get the controller type 2 string controllerName = RequestContext.RouteData.GetRequiredString("controller"); 3 factory = ControllerBuilder.GetControllerFactory(); 4 controller = factory.CreateController(RequestContext, controllerName);
從RequestContext.RouteData中獲取到當前請求的conroller名稱,然后用來作為factory.CreateController的參數;
圖3:
MvcHandler通過ControllerBuilder對象的靜態屬性Current獲取到ControllerBuilder對象實例,顯然ControllerBuilder是一個單例模式的對象;然后通過ControllerBuilder對象獲取到DefaultControllerFactory默認IControllerFactory工廠對象,接着利用DefaultControllerFactory創建出IController對象;
作者:王清培
出處:http://www.cnblogs.com/wangiqngpei557/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。