ASP.NET MVC5学习笔记之Controller同步执行架构分析

  在开始之前,声明一下,由于ASP.NET MVC5正式发布了,后面的分析将基于ASP.NET MVC5最新的源代码。
在前面的内容我们分析了怎样根据路由信息来确定Controller的类型,并最终生成Controller的实例。这一节来了解一下Controller的总体执行分析,以同步执行版本为主。

Controller的继承体系如下图所示:

  

当调用Controller实例的Excecute方法时,实际是调用ControllerBase的Excecute方法,该方法的主要实现代码如下:

 1 protected virtual void Execute(RequestContext requestContext)
 2         {
 3                 
 4             VerifyExecuteCalledOnce();
 5             Initialize(requestContext);
 6             using (ScopeStorage.CreateTransientScope())
 7             {
 8                 ExecuteCore();
 9             }
10         }
View Code

该方法主要为Action的执行做一些初始化工作,Initialize(requestContext)实例化ControllerContext, ScopeStorage.CreateTransientScope方法初始化临时存储环境,这个储存主要用于页面HtmlHlper类中,具体的后面遇到再分析。最后了调用了抽象方法ExecuteCore,在Controller中实现该方法,我们看看该方法的代码:

 1 protected override void ExecuteCore()
 2 {
 3    PossiblyLoadTempData();
 4             try
 5             {
 6                 string actionName = GetActionName(RouteData);
 7                 if (!ActionInvoker.InvokeAction(ControllerContext, actionName))
 8                 {
 9                     HandleUnknownAction(actionName);
10                 }
11             }
12             finally
13             {
14                 PossiblySaveTempData();
15             }
16 }
View Code

PossiblyLoadTempData, PossiblySaveTempData这一对方法主要是帮助处理Controller的TempData属性中临时数据的加载和保存,默认是基于SessionStateTempDataProvider,也就是TempData的值默认保存在Session中,我们可以在会话范围内使用TempData。接下来的代码很简单直接就是调用ActionInvoker的InvokeAction方法并传递上下文参数和action名称。ActionInvoker是一个类型为IActionInvoker的属性, 它内部实现调用CreateActionInvoker方法,具体的实现如下:

return Resolver.GetService<IAsyncActionInvoker>() ?? Resolver.GetService<IActionInvoker>() ?? new AsyncControllerActionInvoker();

Resolver默认情况下是没有注册IActionInvoker的,可见默认情况下IActionInvoker的实例是AsyncControllerActionInvoker。我们现在来具体看一下IActionInvoker的继承体系.如下图所示:

在这里我们仅分析同步版本的ControllerActionInvoker的实现。现在来看看其InvokeAction的方法, 这个方法实现比较复杂,主要的代码如下:

public virtual bool InvokeAction(ControllerContext controllerContext, string actionName)
        {
           
            //省略检查代码
            ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);
            ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);

            if (actionDescriptor != null)
            {
                FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);

                try
                {
                    AuthenticationContext authenticationContext = InvokeAuthenticationFilters(controllerContext, filterInfo.AuthenticationFilters, actionDescriptor);

                    if (authenticationContext.Result != null)
                    {
                        // An authentication filter signaled that we should short-circuit the request. Let all
                        // authentication filters contribute to an action result (to combine authentication
                        // challenges). Then, run this action result.
                        AuthenticationChallengeContext challengeContext = InvokeAuthenticationFiltersChallenge(
                            controllerContext, filterInfo.AuthenticationFilters, actionDescriptor,
                            authenticationContext.Result);
                        InvokeActionResult(controllerContext, challengeContext.Result ?? authenticationContext.Result);
                    }
                    else
                    {
                        AuthorizationContext authorizationContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);
                        if (authorizationContext.Result != null)
                        {
                            // An authorization filter signaled that we should short-circuit the request. Let all
                            // authentication filters contribute to an action result (to combine authentication
                            // challenges). Then, run this action result.
                            AuthenticationChallengeContext challengeContext = InvokeAuthenticationFiltersChallenge(
                                controllerContext, filterInfo.AuthenticationFilters, actionDescriptor,
                                authorizationContext.Result);
                            InvokeActionResult(controllerContext, challengeContext.Result ?? authorizationContext.Result);
                        }
                        else
                        {
                            if (controllerContext.Controller.ValidateRequest)
                            {
                                ValidateRequest(controllerContext);
                            }

                            IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);
                            ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);

                            // The action succeeded. Let all authentication filters contribute to an action result (to
                            // combine authentication challenges; some authentication filters need to do negotiation
                            // even on a successful result). Then, run this action result.
                            AuthenticationChallengeContext challengeContext = InvokeAuthenticationFiltersChallenge(
                                controllerContext, filterInfo.AuthenticationFilters, actionDescriptor,
                                postActionContext.Result);
                            InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters,
                                challengeContext.Result ?? postActionContext.Result);
                        }
                    }
                }
                catch (ThreadAbortException)
                {
                    // This type of exception occurs as a result of Response.Redirect(), but we special-case so that
                    // the filters don't see this as an error.
                    throw;
                }
                catch (Exception ex)
                {
                    // something blew up, so execute the exception filters
                    ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);
                    if (!exceptionContext.ExceptionHandled)
                    {
                        throw;
                    }
                    InvokeActionResult(controllerContext, exceptionContext.Result);
                }

                return true;
            }

            // notify controller that no method matched
            return false;
}    

方法内大致做了以下几件事情:

  1. 调用GetControllerDescriptor方法获取ControllerDescriptor类的实例
  2. 调用FinAction方法获取ActionDescriptor类的实例
  3. 收集应用在Action上所有Filter信息
  4. 执行Filter和对应的Action方法
  5. 执行结果ActionResult并返回, true表示找到Acion, false表示没有匹配的Action

上面的每一条都包含很多处理,这里稍微在较高层解说一下相关的类型,具体的后面每个小节解释一个话题 。

1. ControllerDescriptor和ActionDescriptor分别表示Controller和Action元数据的相关描述类型

2. Filter 是提供了ASP.NET MVC 提供的一种 面向方面(AOP)的编程方式,用于Controler或Action执行前后做一些通用处理,如安全验证,授权,异常处理等.

大概有以下几种Filter类型:

IAuthentiactionFilter 表示自定义验证,这个是ASP.NET MVC5新增的类型, OnAuthentication表示执行验证检查,你也许要更改AuthenticationContext的Principal属性,ASP.NET MVC会检查该属性,发现有更改将使用你设置的Principal, OnAuthenticationChallenge方法有点特别,不管是Action或其它的Filter执行失败或成功都会调用OnAuthenticationChallenge方法(异常除外),OnAuthenticationChallenge方法为你的自定义验证写回Http响应提供机会, 如你也许要改写Http header

public interface IAuthenticationFilter
    {
        /// <summary>Authenticates the request.</summary>
        /// <param name="filterContext">The context to use for authentication.</param>
        void OnAuthentication(AuthenticationContext filterContext);

        /// <summary>Adds an authentication challenge to the current <see cref="ActionResult"/>.</summary>
        /// <param name="filterContext">The context to use for the authentication challenge.</param>
        void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext);
    }

 

IAuthenticationFilter表示授权检查

1 public interface IAuthorizationFilter
2     {
3         void OnAuthorization(AuthorizationContext filterContext);
4     }
View Code

IActionFilter表示Action执行前后做一些处理

1 public interface IActionFilter
2     {
3         void OnActionExecuting(ActionExecutingContext filterContext);
4         void OnActionExecuted(ActionExecutedContext filterContext);
5     }
View Code

IResultFilter表示ActionResult执行前后做一些处理

public interface IResultFilter
    {
        void OnResultExecuting(ResultExecutingContext filterContext);
        void OnResultExecuted(ResultExecutedContext filterContext);
    }
View Code

IExceptionFilter表示异常通用处理

1 public interface IExceptionFilter
2     {
3         void OnException(ExceptionContext filterContext);
4     }
View Code

 

Filter的总体执行流程如下图所示:

 

 下一节分析一下ControllerDescriptor和ActionDescriptor 

posted @ 2014-04-07 12:20  十三  阅读(652)  评论(0编辑  收藏  举报