• 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 

  • 相关阅读:
    Head First 设计模式笔记(第十章 状态模式 & 第十一章 代理模式)
    Head First 设计模式笔记(第八章 模板方法模式 & 第九章 适配器模式)
    Head First 设计模式笔记(第六章 命令模式 & 第七章 适配器模式)
    Head First 设计模式笔记(第四章 工厂模式 & 第五章 单例模式)
    Head First 设计模式笔记(第二章 观察者模式 & 第三章 装饰者模式)
    Head First 设计模式笔记(第一章 设计模式入门)
    高性能MySQL笔记(第十二章 高可用性)
    CF 1514B AND 0, Sum Big
    Dijkstra【模板】【邻接表+优先队列】
    快速幂+矩阵快速幂【模板】
  • 原文地址:https://www.cnblogs.com/jjyjjyjjy/p/MVC5.html
Copyright © 2020-2023  润新知