在开始之前,声明一下,由于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 }
该方法主要为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 }
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的方法, 这个方法实现比较复杂,主要的代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
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 ;
} |
大致做了以下几件事情:
- 调用GetControllerDescriptor方法获取ControllerDescriptor类的实例
- 调用FinAction方法获取ActionDescriptor类的实例
- 收集应用在Action上所有Filter信息
- 执行Filter和对应的Action方法
- 执行结果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
1
2
3
4
5
6
7
8
9
10
|
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 }
IActionFilter表示Action执行前后做一些处理
1 public interface IActionFilter 2 { 3 void OnActionExecuting(ActionExecutingContext filterContext); 4 void OnActionExecuted(ActionExecutedContext filterContext); 5 }
IResultFilter表示ActionResult执行前后做一些处理
public interface IResultFilter { void OnResultExecuting(ResultExecutingContext filterContext); void OnResultExecuted(ResultExecutedContext filterContext); }
IExceptionFilter表示异常通用处理
1 public interface IExceptionFilter 2 { 3 void OnException(ExceptionContext filterContext); 4 }
Filter的总体执行流程如下图所示: