MVC Controller
先回顾下之前的MVC执行过程:
一个HttpRequest是如何被ASP.NET和ASP.NET MVC框架执行的:经过IIS和ASP.NET处理后,Core Routing会首先根据URL匹配物理路径上的文件,如果不能匹配则由核心路由模块执行路由,路由被匹配后,MvcRouteHandler会将这个请求“带入”MVC框架,执行Controller和Action。
Controller是如何被创建以及执行的呢?
我们看一下MVCRouteHander的源码:
namespace System.Web.Mvc { public class MvcRouteHandler : IRouteHandler { private IControllerFactory _controllerFactory; public MvcRouteHandler() { } public MvcRouteHandler(IControllerFactory controllerFactory) { _controllerFactory = controllerFactory; } protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) { requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext)); return new MvcHandler(requestContext); } protected virtual SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext) { string controllerName = (string)requestContext.RouteData.Values["controller"]; if (String.IsNullOrWhiteSpace(controllerName)) { throw new InvalidOperationException(MvcResources.MvcRouteHandler_RouteValuesHasNoController); } IControllerFactory controllerFactory = _controllerFactory ?? ControllerBuilder.Current.GetControllerFactory(); return controllerFactory.GetControllerSessionBehavior(requestContext, controllerName); } #region IRouteHandler Members IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext) { return GetHttpHandler(requestContext); } #endregion } }
MvcRouteHandler的实现仅仅是通过GetHttpHandler方法返回一个MvcHandler实例。
再来看一下MvcHandler的实现:
核心方法依然是ProcessRequest(异步版BeginProceessRequest)。
protected internal virtual void ProcessRequest(HttpContextBase httpContext) { IController controller; IControllerFactory factory; ProcessRequestInit(httpContext, out controller, out factory); try { controller.Execute(RequestContext); } finally { factory.ReleaseController(controller); } }
ProcessRequestInit方法实现了Controller的创建
private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) { // If request validation has already been enabled, make it lazy. This allows attributes like [HttpPost] (which looks // at Request.Form) to work correctly without triggering full validation. // Tolerate null HttpContext for testing. HttpContext currentContext = HttpContext.Current; if (currentContext != null) { bool? isRequestValidationEnabled = ValidationUtility.IsValidationEnabled(currentContext); if (isRequestValidationEnabled == true) { ValidationUtility.EnableDynamicValidation(currentContext); } } AddVersionHeader(httpContext); RemoveOptionalRoutingParameters(); // Get the controller type string controllerName = RequestContext.RouteData.GetRequiredString("controller"); // Instantiate the controller and call Execute factory = ControllerBuilder.GetControllerFactory(); controller = factory.CreateController(RequestContext, controllerName); if (controller == null) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, factory.GetType(), controllerName)); } }
MvcHandler从RouteData中获得controller名字负责创建一个ControllerBuilder的实例,并通过ControllerBuilder的GetControllerFactory返回一个IControllerFactory的实例,这个实例就是DefaultControllerFactory,它的 CreateController方法负责创建需要的Controller实例
// Get the controller type string controllerName = RequestContext.RouteData.GetRequiredString("controller"); // Instantiate the controller and call Execute factory = ControllerBuilder.GetControllerFactory(); controller = factory.CreateController(RequestContext, controllerName);
Controller的执行:
Controller继承自ControllerBase,而Controller又继承自IController,而IController的定义非常简单:
namespace System.Web.Mvc { public interface IController { void Execute(RequestContext requestContext); } }
只有一个Execute()接口。从接口定义可以看出,当Controller被“调用”的时候,应该负责完成Execute方法,参数RequestContext封装了HttpContext。
打开Controller源文件并没有发现Execute()方法的实现,那么它应该在ControllerBase中,查看ControllerBase发现,在,Execute在内部调用ExecuteCore,ExecuteCore作为一个抽象方法,延迟到Controller中实现。ControllerBase只提供了诸如TempData、ViewData等,Controller的ExecuteCore方法真正invoke了action机制,是action的入口。下面的代码是ExecuteCore的实现:
protected override void ExecuteCore() { // If code in this method needs to be updated, please also check the BeginExecuteCore() and // EndExecuteCore() methods of AsyncController to see if that code also must be updated. PossiblyLoadTempData(); try { string actionName = RouteData.GetRequiredString("action"); if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) { HandleUnknownAction(actionName); } } finally { PossiblySaveTempData(); } }
可以看到RouteData在这里又提供了action参数,可以想象InvokeAction方法依靠这个action的名字调用action,并实现诸多验证机制