白话学习MVC(三)页面周期二
这一节就是介绍负责处理请求的MvcHandler的ProcessRequest方法里的两个方法。
public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState { protected virtual void ProcessRequest(HttpContext httpContext) { //使用HttpContextWrapper对HttpContext进行封装,封装的目的是为了解耦以获得可测试性.然后从RequestContext.RouteData中提取Controller名称. HttpContextBase httpContext2 = new HttpContextWrapper(httpContext); this.ProcessRequest(httpContext2); } protected internal virtual void ProcessRequest(HttpContextBase httpContext) { IController controller; IControllerFactory controllerFactory; this.ProcessRequestInit(httpContext, out controller, out controllerFactory); //获取到Controler实例 try {
//当前Controler对象的Action的创建与执行(执行包括:加载TempData, 创建及执行Action,处理Action返回的ActionResult ,保存TempData数据) controller.Execute(this.RequestContext); } finally {
//释放当前Controler对象 controllerFactory.ReleaseController(controller); } } }
下面就来介绍红色字体的两个方法。
第一处红色字体部分:ProcessRequestInit(...)方法
private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) {
//使用指定的 HTTP 上下文来添加版本标头。如:添加一个Http Header: HTTP/1.1 200 OK … X-AspNetMvc-Version: 2.0… this.AddVersionHeader(httpContext); this.RemoveOptionalRoutingParameters();
//RequestContext.RouteData中提取Controller名称。如:Home/Index的话,就获取到Home string requiredString = this.RequestContext.RouteData.GetRequiredString("controller"); factory = this.ControllerBuilder.GetControllerFactory();//获取一个用于创建Controller实例的ControllerFactory.默认DefaultControllerFactory controller = factory.CreateController(this.RequestContext, requiredString);//根据ControllerFactory创建Controller对象。 if (controller == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture,MvcResources.ControllerBuilder_FactoryReturnedNull,new object[] { factory.GetType(), requiredString })); } }
由于使用了out关键字,这个方法中的执行过程中所得到的值,即:赋值给ProcessRequest方法中声明的Controller和ControllerFactory
第二处红色字体部分:controller.excute(...)方法
执行当前Controller实例的excute方法(继承自ControllerBase的方法)。action的创建和执行
// System.Web.Mvc.ControllerBase protected virtual void Execute(RequestContext requestContext) { if (requestContext == null) { throw new ArgumentNullException("requestContext"); } this.VerifyExecuteCalledOnce(); this.Initialize(requestContext); //创建ControllerContext类的一个实例 this.ExecuteCore(); //加载TempData,创建执行Action,处理Action返回的ActionResult,保存TempData }
当前Controller实例的ExecuteCore方法
// System.Web.Mvc.Controller
protected override void ExecuteCore() { this.PossiblyLoadTempData();//加载TempData----------详细可读此博客
try { string requiredString = this.RouteData.GetRequiredString("action"); //从匹配好的路由中获取到访问的Action名称 if (!this.ActionInvoker.InvokeAction(base.ControllerContext, requiredString)) //创建Action,并处理Action返回的ActionResult { this.HandleUnknownAction(requiredString); } } finally { this.PossiblySaveTempData();//保存TempData } }
this.ActionInvoker即:Controller类的ActionInvoker属性。
this.ActionInvoker就是获的一个ControllerActionInvoker实例,该类负责调用控制器的操作方法。
// System.Web.Mvc.Controller public IActionInvoker ActionInvoker { get { if (this._actionInvoker == null) { this._actionInvoker = this.CreateActionInvoker(); //此方法 New了一个ControllerActionInvoker实例,并返回。 } return this._actionInvoker; } set { this._actionInvoker = value; } }
上述执行的this.ActionInvoker.InvokeAction(base.ControllerContext, requiredString)
即:执行ControllerActionInvoker实例的InvokeAction(...)方法
此方法执行的大概流程:
Action的执行包括两部分:Action方法本身的执行;相关筛选器的执行;
- 获取ControllerDescriptor对象,此对象的作用是:封装描述控制器的信息,如控制器的名称、类型和操作。
- 获取ActionDescriptor对象,此对象的作用:提供有关操作方法的信息,如操作方法的名称、控制器、参数、特性和筛选器。
- 查找Controller和Action声明的所有Attribute(筛选器Filter),并执行相关的筛选器。
- 获取Action需要的参数
- 执行Action
- 将ActionResult呈现到客户端
ASP.NET MVC的筛选器是基于AOP(面向方面编程)的设计,我们将一些非业务的逻辑实现在响应的筛选器,并以一种横切(Crosscutting)的方式应用到Action方法上。在Action方法执行前后,这些筛选器会自动执行。ASP.NET MVC提供了AuthorizationFilter,ActionFilter,ResultFilter和ExceptionFilter四种筛选器。
// System.Web.Mvc.ControllerActionInvoker public virtual bool InvokeAction(ControllerContext controllerContext, string actionName) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } if (string.IsNullOrEmpty(actionName)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName"); } ControllerDescriptor controllerDescriptor = this.GetControllerDescriptor(controllerContext);//获取控制器信息 ActionDescriptor actionDescriptor = this.FindAction(controllerContext, controllerDescriptor, actionName);//获取Action信息 if (actionDescriptor != null) { FilterInfo filters = this.GetFilters(controllerContext, actionDescriptor);//检索有关操作筛选器的信息,并返回封装了有用筛选器(Filters)信息的对象。 try {
//使用指定的操作描述符和控制器上下文来调用指定的授权筛选器(Filter)。返回的类型作用:对使用 AuthorizeAttribute 特性时所需的信息进行封装。 AuthorizationContext authorizationContext = this.InvokeAuthorizationFilters(controllerContext, filters.AuthorizationFilters, actionDescriptor);
//如果筛选器AuthorizationFilter不为空
if (authorizationContext.Result != null) { this.InvokeActionResult(controllerContext, authorizationContext.Result);//执行此Action上的筛选器AuthorizationFilter(Action执行之前) } else { if (controllerContext.Controller.ValidateRequest) //如果请求启用了请求验证 { ControllerActionInvoker.ValidateRequest(controllerContext); }
//获取Action所需要的参数 IDictionary<string, object> parameterValues = this.GetParameterValues(controllerContext, actionDescriptor); ActionExecutedContext actionExecutedContext = this.InvokeActionMethodWithFilters(controllerContext, filters.ActionFilters, actionDescriptor, parameterValues); this.InvokeActionResultWithFilters(controllerContext, filters.ResultFilters, actionExecutedContext.Result); } } catch (ThreadAbortException) { throw; } catch (Exception exception) { ExceptionContext exceptionContext = this.InvokeExceptionFilters(controllerContext, filters.ExceptionFilters, exception); if (!exceptionContext.ExceptionHandled) { throw; } this.InvokeActionResult(controllerContext, exceptionContext.Result);//参数二,就是指返回的ActionResult的类型。 } return true; } return false; }
用于处理请求的ASP.NET MVC 框架采用管道式设计,整个处理流程具有三个基本的环节,即:Action方法的执行、生成ActionResult和执行ActionResult。
ActionResult是一个抽象类,其中有一个抽象方法ExcuteResult。
这里的ActionResult是指继承了它,并实现了其抽象方法ExcuteResult。
有:EmptyResult、ContentResult、FileResult、JavaScripResult、JsonResult、ViewResult.....
执行ActionResult就是调用返回的ActionResult类的ExecuteResult方法。
例:假如Action方法中返回的是个ViewResult类型。
Action的返回的ActionResult就是一个ViewResult类。
这个ViewResult类的ExecuteResult(controllerContext)方法被执行。
protected virtual void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) //假如返回的是一个ViewResult类型 { actionResult.ExecuteResult(controllerContext); }
此时请求到ViewResult后,ExecuteResult方法被调用,且看此方法的内部实现:
ViewResult类继承自ViewResultBase中的ExecuteResult方法。
//public class ViewResult : ViewResultBase
//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) { result = FindView(context);//通过视图引擎获取到ViewEngineResult ,此时模板页面【aspx】被加载成了对应的ViewPage类 View = result.View; } TextWriter writer = context.HttpContext.Response.Output; ViewContext viewContext = new ViewContext(context, View, ViewData, TempData, writer); View.Render(viewContext, writer);//通过FindView去搜索这个view找到这个view后通过viewRender(viewcontext,writer)进行渲染 最终生成html返回给客户 。 if (result != null) { result.ViewEngine.ReleaseView(context, View); } }
内部主要是通过ViewResult的FindView方法通过ViewEngine去加载具体的Aspx页面或者是cshtml页面生成对应的page类【针对Aspx】,然后再调用IView接口的Render方法将请求信息+ViewData的信息以等一块渲染成Html并写回到客户端。
在此阶段我们发现IViewEngine内部的实现这是到规定路径下去加载Aspx页面生成对应的ViewPage类。
IView接口的Render方法才是真正的去将Html和数据装配的到一块。
自此请求结束。
执行顺序如图:
此图摘自:http://kb.cnblogs.com/page/47224/