標籤:des style class blog code http
在上一篇"ASP.NET MVC請求處理管道生命週期的19個關鍵環節(7-12) ",體驗了7-12關鍵環節,本篇繼續。
⒀當請求到達UrlRoutingModule的時候,UrlRoutingModule取出請求中的Controller、Action等RouteData資訊,與路由表中的所有規則進行匹配,若匹配,把請求交給IRouteHandler,即MVCRouteHandler
MVCRouteHandler是用來產生實現IHttpHandler介面的MvcHandler:
namespace System.Web.Routing{ public interface IRouteHandler { IHttpHandler GetHttpHandler(RequestContext requestContext); }}
UrlRoutingModule如何把請求交給MVCRouteHandler?
通過分析UrlRoutingModule的源碼可以看到:
//通過RouteCollection的靜態方法GetRouteData擷取到封裝路由資訊的RouteData執行個體
RouteData routeData = this.RouteCollection.GetRouteData(context);
//再從RouteData中擷取MVCRouteHandler
IRouteHandler routeHandler = routeData.RouteHandler;
為什麼可以從RouteData中拿到MVCRouteHadnler呢?
因為當我們在HttpApplication的第一個管道事件,使用MapRoute()方法註冊路由的時候,已經通過Route類的建構函式把MVCRouteHandler注入到路由中了。
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) { if (routes == null) { throw new ArgumentNullException("routes"); } if (url == null) { throw new ArgumentNullException("url"); } Route route = new Route(url, new MvcRouteHandler()) { Defaults = new RouteValueDictionary(defaults), Constraints = new RouteValueDictionary(constraints), DataTokens = new RouteValueDictionary() }; if ((namespaces != null) && (namespaces.Length > 0)) { route.DataTokens["Namespaces"] = namespaces; } routes.Add(name, route); return route; }
⒁MVCRouteHandler把請求交給MvcHandler
還是從UrlRoutingModule的源碼可以看到,通過HttpHandler的GetHttpHandler()方法擷取到了實現了IHttpHandler介面的MVCHandler:
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);context.RemapHandler(httpHandler);
MvcHandler的部分源碼為:
public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState{ protected internal virtual void ProcessRequest(HttpContextBase httpContext) { SecurityUtil.ProcessInApplicationTrust(() => { IController controller; IControllerFactory factory; ProcessRequestInit(httpContext, out controller, out factory);//初始化了ControllerFactory try { controller.Execute(RequestContext); } finally { factory.ReleaseController(controller); } }); } private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) { bool? isRequestValidationEnabled = ValidationUtility.IsValidationEnabled(HttpContext.Current); if (isRequestValidationEnabled == true) { ValidationUtility.EnableDynamicValidation(HttpContext.Current); } AddVersionHeader(httpContext); RemoveOptionalRoutingParameters(); string controllerName = RequestContext.RouteData.GetRequiredString("controller"); factory = ControllerBuilder.GetControllerFactory(); controller = factory.CreateController(RequestContext, controllerName); if (controller == null) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,MvcResources.ControllerBuilder_FactoryReturnedNull,factory.GetType(),controllerName)); } }}
⒂從以上可以看出:首先通過ControllerBuilder的靜態方法GetControllerFactory擷取到實現IControllerFactory介面的ControllerFactory,然後根據從上下文中的路由資料中拿到controller名稱,並據此建立實現IController介面的Controller
Controller派生於ControllerBase, 而ControllerBase實現了IController介面。ControllerBase的部分源碼如下:
public abstract class ControllerBase : IController{ protected virtual void Execute(RequestContext requestContext) { if (requestContext == null) { throw new ArgumentNullException("requestContext"); } if (requestContext.HttpContext == null) { throw new ArgumentException( MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, "requestContext"); } VerifyExecuteCalledOnce(); Initialize(requestContext); using (ScopeStorage.CreateTransientScope()) { ExecuteCore(); } } protected abstract void ExecuteCore(); ......}
從中可以看成:
● 每次調用controller,都會執行基類ControllerBase的Execute()方法
● Execute()方法又會調用ExecuteCore()這個抽象方法
● ExecuteCore()這個抽象方法的實現被定義在Controller中
● 在Controller中的ExecuteCore()方法會調用ActionInvoker的InvokeAction()方法
⒃ActionInvoker激發Action方法
ActionInvoker實現了IActionInvoker介面:
public interface IActionInvoker{ bool InvokeAction(ControllerContext controllerContext, string actionName);}
MVC預設的ActionInvoker是ControllerActionInvoker。
在Controller類中,提供了類型為IActionInvoker的屬性ActionInvoker,當執行ExecuteCore()方法時會讓這個ActionInvoker調用InvokeAction()方法激發Action。如下:
public class Controller{ ...... private IActionInvoker _actionInvoker; public IActionInvoker ActionInvoker { get { if(_actionInvoker == null) { _actionInvoker = CreateActionInvoker(); } return _actionInvoker; } set { _actionInvoker = value; } } protected virtual IActionInvoker CreateActionInvoker() { return new ControllerActionInvoker(); } public override void ExecuteCore() { ActionInvoker.InvokeAction(...); } .....}
ActionInvoker在執行InvokeAction()方法時會需要有關Controller和Action的相關資訊,實際上,Controller資訊(比如Controller的名稱、類型、包含的Action等)被封裝在ControllerDescriptor這個類中,Action資訊(比如Action的名稱、參數、屬性、過濾器等)被封裝在ActionDescriptor中。
另外,ActionDescriptor還提供了一個FindAction()方法,用來找到需要被執行的Action。
⒄ActionInvoker在執行InvokeAction()方法返回ActionResult
ActionResult是一個抽象類別:
public abstract class ActionResult{ public abstract void ExecuteResult(ControllerContext context);}
如果ActionResult是非ViewResult,比如JsonResult, ContentResult,這些內容將直接被輸送到Response響應流中,顯示給用戶端;如果是ViewResult,就會進入下一個渲染視圖環節。
⒅ViewEngine找到需要被渲染的視圖
預設的有Razor View Engine和Web Form View Engine,實現IViewEngine介面。
IViewEngine介面方法:
● FindPartialView
● FindView
● ReleaseView
如果要建立自訂View Engine,只需要派生於VirtualPathProviderViewEngine這個類。
⒆View被載入成WebViewPage<TModel>類型,並渲染產生Html
調用ViewResult的ExecuteResult()方法,通過IView的Render()方法渲染成Html。
public abstract class ViewResultBase : ActionResult{ public override void ExecuteResult(ControllerContext context) { if (context == null) { throw new ArgumentNullException("context"); } if (String.IsNullOrEmpty(ViewName)) { ViewName = context.RouteData.GetRequiredString("action"); } ViewEngineResult result = null; if (View == null) { //通過視圖引擎擷取到ViewEngineResult ,此時模板頁面【aspx】被載入成了WebViewPage<TModel> result = FindView(context); View = result.View; } TextWriter writer = context.HttpContext.Response.Output; ViewContext viewContext = new ViewContext(context, View, ViewData, TempData, writer); View.Render(viewContext, writer); if (result != null) { result.ViewEngine.ReleaseView(context, View); } }}
completed~~