asp.net MVC 应用程序的生命周期(下)

时间:2022-01-26 17:26:17

看看上面的UrlRoutingModule源码里面是怎么实现Init方法的,Init()方法里面我标注红色的地方:

application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);

这一步至关重要哈,看到没有,就是对我们在HttpApplication那19个事件中的PostResolveRequestCache事件的注册。

注册的方法是OnApplicationPostResolveRequestCache事件。也就是说HttpApplication对象在执行到PostResolveRequestCache这个事件的时候,就会执行OnApplicationPostResolveRequestCache事件。决定是MVC机制处理请求的关键所在就是OnApplicationPostResolveRequestCache事件。

从源码中我们看出,OnApplicationPostResolveRequestCache事件执行的时候,最终执行了PostResolveRequestCache这个方法。最关键的地方呢就在这里了。

当请求到达UrlRoutingModule的时候,UrlRoutingModule取出请求中的Controller、Action等RouteData信息,与路由表中的所有规则进行匹配,若匹配,把请求交给IRouteHandler,即MVCRouteHandler。

我们可以看下UrlRoutingModule的源码来看看,以下是几句核心的代码:

我们再分析一下这个方法的源码:

public virtual void PostResolveRequestCache(HttpContextBase context)

{

// 通过RouteCollection的静态方法GetRouteData获取到封装路由信息的RouteData实例

RouteData routeData = this.RouteCollection.GetRouteData(context);

if (routeData != null)

{

// 再从RouteData中获取MVCRouteHandler

IRouteHandler routeHandler = routeData.RouteHandler;

......

if (!(routeHandler is StopRoutingHandler))

{

......

// 调用 IRouteHandler.GetHttpHandler(),获取的IHttpHandler 类型实例,它是由 IRouteHandler.GetHttpHandler获取的,这个得去MVC的源码里看

IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);

......

// 合适条件下,把之前将获取的IHttpHandler 类型实例 映射到IIS HTTP处理管道中

context.RemapHandler(httpHandler);

}

}

}

看到了吧,通过路由规则,返回的不为空,说明匹配正确,关于路由规则的匹配,说起来也不短,这里就不大幅介绍,有时间下次再开篇详解路由机制。匹配成功后,返回一个RouteData类型的对象,RouteData对象都有些什么属性呢?看看这行源码: IRouteHandler routeHandler = routeData.RouteHandler;或者看源码我们知道,RouteDate有一个RouteHandler属性。

asp.net MVC 应用程序的生命周期(下)

那么UrlRouting Module是如何选择匹配规则的呢?

asp.net MVC 应用程序的生命周期(下)

我们看看我们新建的MVC应用程序,在App_Start文件夹下面有一个RouteConfig.cs类,这个类的内容如下:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Mvc;

using System.Web.Routing;

namespace ApiDemo

{

public class RouteConfig

{

public static void RegisterRoutes(RouteCollection routes)

{

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapRoute(

name: "Default",

url: "{controller}/{action}/{id}",

defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }

);

}

}

}

我们在这个类里面,主要是给路由表添加路由规则。在看看上面的UrlRoutingModule类,里面有一个RoutCollection属性,所以UrlRoutingModule能够获取路由表中的所有规则,这里值得注意的是,路由规则的匹配是有顺序的,如果有多个规则都能够匹配,UrlRoutingModule至选择第一个匹配的规则就返回,不再继续往下匹配了。相反的如果一个请求,没有匹配到任何路由,那么该请求就不会被处理。

这里返回的RouteData里的RouteHandler就是MVCRouteHandler。为什么呢?那我们继续往下看RouteHandler。

RouteHandler

生成MvcHander

在上面路由匹配的过程中,与匹配路由相关联的MvcRouteHandler ,MvcRouteHandler 实现了IRouteHandler 接口。

MvcRouteHandler 主要是用来获取对MvcHandler的引用。MvcHandler实现了IhttpHandler接口。

MVCRouteHandler的作用是用来生成实现IHttpHandler接口的MvcHandler。而我们前面说过最终处理请求的都是相对应的HttpHandler。

那么处理MVC请求的自然就是这个MvcHandler。所以这里返MvcRouteHandler至关重要:

那么,MvcRouteHandler从何而来呢?众所周知,ASP.NET MVC项目启动是从Global中的Application_Start()方法开始的,那就去看看它:

public class MvcApplication : System.Web.HttpApplication

{

protected void Application_Start()

{

RouteConfig.RegisterRoutes(RouteTable.Routes);

BundleConfig.RegisterBundles(BundleTable.Bundles);

}

}

public class RouteConfig

{

public static void RegisterRoutes(RouteCollection routes)

{

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

System.Web.Mvc.RouteCollectionExtensions

routes.MapRoute(

name: "Default",

url: "{controller}/{action}/{id}",

defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }

);

}

}

看看我上面标红的代码:这是路由注册,玄机就在这里。那我们去看看MapRoute源码就知道咯:

public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)

{

......

Route route = new Route(url, new MvcRouteHandler())             {

Defaults = new RouteValueDictionary(defaults),

Constraints = new RouteValueDictionary(constraints),

DataTokens = new RouteValueDictionary()

};

......

return route;

}

看看我们5-8行代码,在MVC应用程序里,在路由注册的时候,我们就已经给他一个默认的HttpRouteHandler对象,就是 New MvcRouteHandler().现在我们反推回去,我们MVC程序在路由注册的时候就已经确定了HttpRouteHandler为MvcRouteHandler,那么当我们在前面PostResolveRequestCache方法里,当我们的请求与路由匹配成功后,自然会返回的是MvcRouteHandler。

好啦,MvcRouteHandler生成了。那么MvcRouteHandler能做什么呢?又做了什么呢?

再回头看看 PostResolveRequestCache方法,在成功获取到IHttpRouteHandler对象即MvcRouteHandler之后,又做了下面这一个操作:

IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);

我们看看这个IHttpHandler 的源码:

namespace System.Web.Routing

{

public interface IRouteHandler

{

IHttpHandler GetHttpHandler(RequestContext requestContext);

}

}

有一个GetHttpHandler的方法,恰好就调用了这个方法。那我们看看MvcRouteHandler是怎么实现这个GetHttpHandler的呢:

public class MvcRouteHandler : IRouteHandler

{

// Fields

private IControllerFactory _controllerFactory;

// Methods

public MvcRouteHandler()

{

}

public MvcRouteHandler(IControllerFactory controllerFactory)

{

this._controllerFactory = controllerFactory;

}

protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)

{

requestContext.HttpContext.SetSessionStateBehavior(this.GetSessionStateBehavior(requestContext));

return new MvcHandler(requestContext);

}

protected virtual SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext)

{

string str = (string) requestContext.RouteData.Values["controller"];

if (string.IsNullOrWhiteSpace(str))

{

throw new InvalidOperationException(MvcResources.MvcRouteHandler_RouteValuesHasNoController);

}

IControllerFactory factory = this._controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();

return factory.GetControllerSessionBehavior(requestContext, str);

}

IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)

{

return this.GetHttpHandler(requestContext);

}

}

看第16-20行代码,这时候应该明白了吧。顺理成章的返回了MvcHandler对象。记得我们前面说过,请求最终是被相对应的HttpHander对象处理的。

MvcHandler就是那个用来处理Mvc请求的HttpHandler。MvcRouteHandler把请求交给了MvcHandler去做请求处理管道中后续事件的处理操作了。

下面我们就看看MvcHandler做了些什么:

MvcHandler

MvcHandler就是最终对request进行处理。

MvcHandler的定义如下:

asp.net MVC 应用程序的生命周期(下)

我们可以看到MvcHandler就是一个普通的Http  Handler.我们知道一个http handler需要实现一个ProcessRequest()的方法,这个方法就是处理request的核心。所以MvcHandler实现了ProcessRequest()方法。

ProcessRequest主要功能:

(1)在ASP.NET MVC中,会调用MvcHandler的ProcessRequest()方法,此方法会激活具体请求的Controller类对象,触发Action方法,返回ActionResult实例。

(2)如果ActionResult是非ViewResult,比如JsonResult, ContentResult,这些内容将直接被输送到Response响应流中,显示给客户端;如果是ViewResult,就会进入下一个渲染视图环节。

(3)在渲染视图环节,ViewEngine找到需要被渲染的视图,View被加载成WebViewPage<TModel>类型,并渲染生成Html,最终返回Html。

ProcessRequest()定义如下:

// Copyright (c) Microsoft Open Technologies, Inc.<pre>// All rights reserved. See License.txt in the project root for license information.

void IHttpHandler.ProcessRequest(HttpContext httpContext)

{

ProcessRequest(httpContext);

}

protected virtual void ProcessRequest(HttpContext httpContext)

{

HttpContextBase iHttpContext = new HttpContextWrapper(httpContext);

ProcessRequest(iHttpContext);

}

protected internal virtual void ProcessRequest(HttpContextBase httpContext) {

SecurityUtil.ProcessInApplicationTrust(() => {

IController controller;

IControllerFactory factory;

ProcessRequestInit(httpContext, out controller, out factory);

try

{

controller.Execute(RequestContext);

}

finally

{

factory.ReleaseController(controller);

}

});

}

从上面的代码可以看出调用了一个ProcessRequestInit()方法,定义如下:

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.

bool? isRequestValidationEnabled =

ValidationUtility.IsValidationEnabled(HttpContext.Current);

if (isRequestValidationEnabled == true) {

ValidationUtility.EnableDynamicValidation(HttpContext.Current);

}

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));

}

}

在ProcessRequestInit()方法中首先创建了ControllerFactory()的对象 factory.然后ControllerFactory创建了相关Controller的实例.最终调用了Controller的Excute()方法。

好我们再来看看ControllerFactory:

ControllerFactory

主要是用来生成Controller对象

ControllerFactory实现了接口IControllerFactory.

Controller

到这里我们大概就知道了,MvcHandler通过ProcessRequest()方法最终创建了Controller对象,这里我们都应该知道,Controller里面包含很多的Action方法,每一次请求至少一个Action方法会被调用。

为了明确的实现IController接口,框架里面有一个ControllerBase的类已经实现了IController接口,其实我们自己的Controller也可以不继承ControllerBase,只要实现IController接口即可。

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对象实际上使用ActionInvoker来调用Action方法的,当Controller对象被创建后,会执行Controller对象的基类ControllerBase类里面的Excute方法。Excute方法又调用了ExcuteCore()方法。Controller类里面实现了ExcuteCore()方法。ExcuteCore调用了ActionInvoker的InvokerAction方法来调用Action方法。

ActionInvoker

ActionInvoker方法有很重要的责任来查找Controller中的Action方法并且调用。

asp.net MVC 应用程序的生命周期(下)

ActionInvoker是一个实现了IActionInvoker接口的对象:

bool InvokeAction(

ControllerContext controllerContext,

string actionName

)

Controller类里面暴露了一个ActionInvoker 属性,会返回一个ControllerActionInvoker 。ActionInvoker通过CreateActionInvoker()方法来创建ControllerActionInvoker对象。

public IActionInvoker ActionInvoker {

get {

if (_actionInvoker == null) {

_actionInvoker = CreateActionInvoker();

}

return _actionInvoker;

}

set {

_actionInvoker = value;

}

}

protected virtual IActionInvoker CreateActionInvoker() {

return new ControllerActionInvoker();

}

我们看到CreateActionInvoker()是一个Virtual方法,我们可以实现自己的ActionInvoker.

ActionInvoker类需要匹配Controller中详细的Action来执行,而这些详细的信息是由ControllerDescriptor 提供的。ControllerDescriptor 和ActionDescriptor在ActionInvoker中扮演重要的角色。这两个分别是对Controler和Action的详细描述。ControllerDescriptor 描述了Controller的相关信息比如name,action,type等。

ActionDescriptor 描述了Action相关的详情,比如name,controller,parameters,attributes和fiflters等。

ActionDescriptor 中一个中要的方法就是FindAction(),这个方法返回一个ActionDescriptor 对象,所以ActionInvoker知道该调用哪个Action。

ActionResult

到目前为止,我们看到了Action方法被ActionInvoker调用。所有的Action方法有一个特性,就是返回一个ActionResult类型的数据。

public abstract class ActionResult

{

public abstract void ExecuteResult(ControllerContext context);

}

ExecuteResult()是一个抽象方法,所以不同的子类可以提供不同的ExecuteResult()实现。

ActionResult执行后响应输出到客户端。

ViewEngine

ViewResult几乎是大部分应用程序的返回类型,主要通过ViewEngine引擎来展示view的。ViewEngine可能主要就是生成Html元素的引擎。

Framwork提供了2种引擎,Razor View Engine 和Web Form View Engine.如果你想自定义引擎,你可以创建一个引擎只要实现IViewEngine接口即可。

asp.net MVC 应用程序的生命周期(下)

IViewEngine 有下面几个方法:

1、FindPartialView :当controller需要返回一个PartialView的时候,FindPartialView方法 就会被调用。

2、FindView

3、ReleaseView :主要用来有ViewEngine释放资源

ViewResultBase 和ViewResult是比较重要的两个类。ViewResultBase 包含下面的实现代码:

if (View == null)

{

result = FindView(context); //calls the ViewResult's FindView() method

View = result.View;

}

ViewContext viewContext = new ViewContext(context, View, ViewData, TempData);

View.Render(viewContext, context.HttpContext.Response.Output);

protected abstract ViewEngineResult FindView(ControllerContext context); //this is implemented by

//the ViewResult

protected override ViewEngineResult FindView(ControllerContext context)

{

ViewEngineResult result = ViewEngineCollection.FindView(context, ViewName, MasterName);

if (result.View != null)

{

return result;

}

//rest of the code omitted

}

当ViewResult的方法ExecuteResult被调用后,ViewResultBase 的ExecuteResult 方法被调用,然后ViewResultBase 调用ViewResult的FindView 。

紧接着ViewResult 返回ViewEngineResult,之后ViewEngineResult调用Render()方法来绘制html输出响应。

总结:如果我们理解了整个过程中发生了什么,哪些类和哪些方法被调用,我们就可以在需要扩展的地方轻松的进行扩展。