白话学习MVC(三)页面周期二

这一节就是介绍负责处理请求的MvcHandlerProcessRequest方法里的两个方法。

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实例,该类负责调用控制器的操作方法。

View Code
// 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方法本身的执行;相关筛选器的执行;

  1. 获取ControllerDescriptor对象,此对象的作用是:封装描述控制器的信息,如控制器的名称、类型和操作。
  2. 获取ActionDescriptor对象,此对象的作用:提供有关操作方法的信息,如操作方法的名称、控制器、参数、特性和筛选器。
  3. 查找Controller和Action声明的所有Attribute(筛选器Filter),并执行相关的筛选器。
  4. 获取Action需要的参数
  5. 执行Action
  6. 将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和数据装配的到一块。

自此请求结束。

执行顺序如图:
result

此图摘自:http://kb.cnblogs.com/page/47224/

 

posted @ 2013-03-25 22:44  武沛齐  阅读(1084)  评论(0编辑  收藏  举报