上篇文章从整体上介绍了ASP.NET MVC的请求处理流程,大概分为“路由”、“控制器”、“视图”以及“模型”,下面便按照流程依次展开。
一、路由
提到路由,实际上分为ASP.NET的核心路由引擎和MVC路由。核心路由引擎是不开源的(微软留了一手),它负责解析浏览器端传来的URL,并为之分配控制器。
这里我们要分析的是MVC路由,即MvcRouteHandler,该类显式实现了IRouteHandler接口,它的GetHttpHandler方法返回了一个MvcHandler对象,而MvcHandler实现了IHttpHandler接口,所以它包含一个ProcessRequest方法,如下:
protected internal virtual voidProcessRequest(HttpContextBase httpContext) { IController controller; IControllerFactory factory; ProcessRequestInit(httpContext, outcontroller, out factory); try { controller.Execute(RequestContext); } finally { factory.ReleaseController(controller); } }
代码段 1
这个ProcessRequest方法相信大家都会觉得脸熟吧?不错,我们在ASP.NET WebForm开发中,经常会用到一般处理程序,即.ashx文件,这个文件中的类就实现了IHttpHandler接口,也有一个ProcessRequest方法。
好了,这个ProcessRequest方法就是路由定位到控制器后,执行的核心操作。也就是说,controller.Execute(RequestContext)语句的执行,导致了HTML响应的生成及回送浏览器。简单吧?我们在这里只是提到了最核心、最骨架的部分,没有涉及细节,为的就是不让大家被细枝末节所干扰。接下来就该分析控制器了。
二、控制器
代码段1中的controller是接口IController的一个实例,而接口IController很简单,只有一个方法如下:
<pre name="code" class="csharp"> public interface IController { void Execute(RequestContextrequestContext); }
代码段 2
Controller的实例化是由【ProcessRequestInit(httpContext, out controller, out factory)】这个语句完成的,下面我们看一下ProcessRequestInit这个方法中实例化Controller的部分,不考虑细节,直奔主题。
private voidProcessRequestInit(HttpContextBase httpContext, out IController controller, outIControllerFactory factory) { // If request validation hasalready been enabled, make it lazy. This allows attributes like [HttpPost](which looks // at Request.Form) to workcorrectly without triggering full validation. // Tolerate null HttpContext fortesting. 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 andcall Execute factory =ControllerBuilder.GetControllerFactory(); controller =factory.CreateController(RequestContext, controllerName); if (controller == null) { throw newInvalidOperationException( String.Format( CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, factory.GetType(), controllerName)); } }
代码段 3
代码段3中的【controller = factory.CreateController(RequestContext,controllerName)】语句,就是实例化控制器的地方,这里根据路由系统定位的控制器信息(即我们在Controller文件夹中定义的各个控制器类),利用反射创建控制器对象。当然,这里面还有很多实现细节,比如控制器工厂、请求验证等,为了保持脉络清晰,在此略过,下文再谈。
IController接口在MVC中有若干个内置的实现类,包括ControllerBase以及IAsyncController,而Controller是ControllerBase的子类,是我们最常用的控制器基类。
其实,我们完全可以自己实现IController,例如:
publicclass MyController : IController { public voidExecute(RequestContext requestContext) { requestContext.HttpContext.Response.Write(“Hello”); } }
代码段 4
怎么样?SoEasy!完全可以不用什么高大上的Razor引擎之类的东东。但是这样做会导致极强的耦合,我们需要自己解析拼接HTML,这可是个累人的活儿。
我们还是看看MVC的Controller类是怎么实现的吧。Controller继承于ControllerBase类,ControllerBase实现了Execute方法(代码段5),而Controller重写了ControllerBase的ExecuteCore方法(代码段6)。
protected virtual voidExecute(RequestContext requestContext) { if (requestContext == null) { throw newArgumentNullException("requestContext"); } if (requestContext.HttpContext ==null) { throw newArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext,"requestContext"); } VerifyExecuteCalledOnce(); Initialize(requestContext); using(ScopeStorage.CreateTransientScope()) { ExecuteCore(); } }
代码段 5
protected override void ExecuteCore() { // If code in this method needs tobe updated, please also check the BeginExecuteCore() and // EndExecuteCore() methods ofAsyncController to see if that code also must be updated. PossiblyLoadTempData(); try { string actionName =GetActionName(RouteData); if(!ActionInvoker.InvokeAction(ControllerContext, actionName)) { HandleUnknownAction(actionName); } } finally { PossiblySaveTempData(); } }
代码段 6
我们看一下ExecuteCore方法,其核心语句是【if (!ActionInvoker.InvokeAction(ControllerContext, actionName))】,这条语句就是执行某个具体Action的地方,该语句执行完后,HTTP响应就被发回了浏览器。这里用到了ActionInvoker.InvokeAction方法,其中的ActionInvoker是IActionInvoker接口的实例,该接口定义也相当简单,如下:
public interface IActionInvoker { bool InvokeAction(ControllerContextcontrollerContext, string actionName); }
代码段 7
MVC内置的IActionInvoker实现类有ControllerActionInvoker和IAsyncActionInvoker,后者是异步类,先不管它,这里我们重点分析一下ControllerActionInvoker。
ControllerActionInvoker的InvokeAction方法实现较为复杂,有大概90行代码。今天有点累了,明天继续。