深入分析 ASP.NET Mvc 1.0 – 3. Controller.Execute(Request)-ActionInvoker.InvokeAction()
上次讲到Controller中的ExecuteCore方法分为三个部执行:
- TempData.Load(ControllerContext, TempDataProvider)
- ActionInvoker.InvokeAction(ControllerContext, actionName)
- TempData.Save(ControllerContext, TempDataProvider)
其中第1, 第3是关于TempData的Load与Save操作,在上一篇有已过深入的分析,这里只分析ActionInvoker.InvokeAction(ControllerContext, actionName) ,首先看一下InvokeAction方法的源码:
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 = GetControllerDescriptor(controllerContext);
ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);
if (actionDescriptor != null) {
FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);
try {
AuthorizationContext authContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);
if (authContext.Result != null) {
// the auth filter signaled that we should let it short-circuit the request
InvokeActionResult(controllerContext, authContext.Result);
}
else {
if (controllerContext.Controller.ValidateRequest) {
ValidateRequest(controllerContext.HttpContext.Request);
}
IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);
ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);
InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, 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;
}
- 获取ControllerDescriptor对象
- 获取ActionDescriptor对象
- 查找Controller和Action声明的所有Attribute
- 获取Action需要的参数
- 执行Action
- 将ActionResult呈现到客户端
1. 获取ControllerDescriptor对象
ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);
GetControllerDescriptor(…)方法的代码是这样的:protected virtual ControllerDescriptor GetControllerDescriptor(ControllerContext controllerContext) {DescriptorCache是ControllerDescriptorCache一个静态实例,它是以Controller的type为Key用于保存ControllerDescriptor对象的缓存容器,看一下GetDescriptor(…)的代码
Type controllerType = controllerContext.Controller.GetType();
ControllerDescriptor controllerDescriptor = DescriptorCache.GetDescriptor(controllerType);
return controllerDescriptor;
}internal sealed class ControllerDescriptorCache : ReaderWriterCache<Type, ControllerDescriptor> {调用父类的FetchOrCreateItem(…)方法并传递两个参数: Type, Func<RelfectedControllerDescriptor>, 再深入到FetchOrCreateItem(…)的代码:
public ControllerDescriptorCache() {
}
public ControllerDescriptor GetDescriptor(Type controllerType) {
return FetchOrCreateItem(controllerType, () => new ReflectedControllerDescriptor(controllerType));
}
}protected TValue FetchOrCreateItem(TKey key, Func<TValue> creator) {
// first, see if the item already exists in the cache
_rwLock.AcquireReaderLock(Timeout.Infinite);
try {
TValue existingEntry;
if (_cache.TryGetValue(key, out existingEntry)) {
return existingEntry;
}
}
finally {
_rwLock.ReleaseReaderLock();
}
// insert the new item into the cache
TValue newEntry = creator();
_rwLock.AcquireWriterLock(Timeout.Infinite);
try {
TValue existingEntry;
if (_cache.TryGetValue(key, out existingEntry)) {
// another thread already inserted an item, so use that one
return existingEntry;
}
_cache[key] = newEntry;
return newEntry;
}
finally {
_rwLock.ReleaseWriterLock();
}
}在缓存中查找数据,如果数据不存在,那么就执行create()方法来生成ReflectedControllerDescriptor类的一个实例,并将这个实例存放到缓存中然后返回这个实例。
特别指出,这个方法的两个try语句块内部都使用_cache.TryGetValue(key, out existingEntry)来尝试获取数据:
- 第一个try是真正从缓存中读取数据,用了AcquireReaderLock锁;这里使用AcquireReaderLock的目地是当其它线程已经使用AcquireWriterLock锁向缓存中写数据时,使这里将处于等待状态,真到使用ReleaseWriterLock()方法释放AcquireWriterLock锁或使用ReleaseLock()方法将锁的数量清0后,才会从等待状态变为执行状态, 继续执行后面的代码。
- 而第二个try语句用了AcquireWriterLock锁,并再次从缓存中读取数据,这样做是因为在_rwLock.AcquireWriterLock(Timeout.Infinite)之前其它线程有可能已经将ReflectedControllerDescriptor对象存放到缓存中。AcquireWriterLock锁的作用是如果没有从缓存中取得数据以保证creator()方法创建的对象可以以“独占”方式被写入到缓存中。
2. 获取ActionFilter对象
ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);
这里调用了FindAction(…)方法,FndAction方法又调用了ReflectedControllerDescriptor的FindAction方法:
public override ActionDescriptor FindAction(ControllerContext controllerContext, string actionName) {
if (controllerContext == null) {
throw new ArgumentNullException("controllerContext");
}
if (String.IsNullOrEmpty(actionName)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
}
MethodInfo matched = _selector.FindActionMethod(controllerContext, actionName);
if (matched == null) {
return null;
}
return new ReflectedActionDescriptor(matched, actionName, this);
}调用_selector.FindActionMethod(…)返回MethodInfo对象,这个MethodInfo就是action的MethodInfo形式。
_selector是ActionMethodSelector类的一个实例,在ReflectedControllerDescriptor的构造函数中实例化这个对象,并且在ActionMethodSelector的构造函数中调用PopulateLookupTables()方法
private void PopulateLookupTables() {逻辑很清析,找到所有的Action,将声明了ActionNameAttribute的Action放入AliasedMethods数组中;将没有声明的ActionNameAttribute的放入NonAliasedMethods数组中(声明了NonActionAttribute的Action也会被存入NonAliasedMethods数组中)。
MethodInfo[] allMethods = ControllerType.GetMethods(BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public);
MethodInfo[] actionMethods = Array.FindAll(allMethods, IsValidActionMethod);
AliasedMethods = Array.FindAll(actionMethods, IsMethodDecoratedWithAliasingAttribute);
NonAliasedMethods = actionMethods.Except(AliasedMethods).ToLookup(method => method.Name, StringComparer.OrdinalIgnoreCase);
}再看一下_selector.FindActionMethod(…)做了哪些操作
public MethodInfo FindActionMethod(ControllerContext controllerContext, string actionName) {
List<MethodInfo> methodsMatchingName = GetMatchingAliasedMethods(controllerContext, actionName);
methodsMatchingName.AddRange(NonAliasedMethods[actionName]);
List<MethodInfo> finalMethods = RunSelectionFilters(controllerContext, methodsMatchingName);
switch (finalMethods.Count) {
case 0:
return null;
case 1:
return finalMethods[0];
default:
throw CreateAmbiguousMatchException(finalMethods, actionName);
}
}
在AliasedMethods和NonAliasedMethods数组中找到与变量actionName相符的MethodInfo对象并保存到methodsMatchingName中,执行RunSelectionFilters(…)方法来过滤这些MethodInfo
private static List<MethodInfo> RunSelectionFilters(ControllerContext controllerContext, List<MethodInfo> methodInfos) {
// remove all methods which are opting out of this request
// to opt out, at least one attribute defined on the method must return false
List<MethodInfo> matchesWithSelectionAttributes = new List<MethodInfo>();
List<MethodInfo> matchesWithoutSelectionAttributes = new List<MethodInfo>();
foreach (MethodInfo methodInfo in methodInfos) {
ActionMethodSelectorAttribute[] attrs = (ActionMethodSelectorAttribute[])methodInfo.GetCustomAttributes(typeof(ActionMethodSelectorAttribute), true /* inherit */);
if (attrs.Length == 0) {
matchesWithoutSelectionAttributes.Add(methodInfo);
}
else if (attrs.All(attr => attr.IsValidForRequest(controllerContext, methodInfo))) {
matchesWithSelectionAttributes.Add(methodInfo);
}
}
// if a matching action method had a selection attribute, consider it more specific than a matching action method
// without a selection attribute
return (matchesWithSelectionAttributes.Count > 0) ? matchesWithSelectionAttributes : matchesWithoutSelectionAttributes;
}action上声明的attribute如果是ActionMethodSelectorAttribute类型或是继承了ActionMethodSelectorAttribute的attribute都会被存入matchesWithSelectionAttributes中,其它的都存入matchesWithoutSelectionAttributes 中。但是能被存放到matchesWithSelectionAttributes的attribute必须有一个条件,就是IsValidForRequest(…)方法的返回值必须是true,而NonActionAttribute.IsValidForRequest(…)的返回值为false,所以它不会被存入matchesWithSelectionAttributes中。最后的返回结果是以matchesWithSelectionAttributes优先,也就是同名方法中声明了AcceptVerbsAttribute的MethodInfo会被优先返回。
说明:RunSelectionFilters(…)方法其实是为了过滤AcceptVerbsAttributes和NonActionAttribute
- NonActionAttribute: 不会被放入到返回的List<MethodInfo>中
- AcceptVerbsAttributes:如果同名的action使用相同verb(都是get或post)那么返回的List<MethodInfo>会有多个MethodInfo对象,这会导致AmbiguousMatchException异常被抛出,也就是说在一个Controller中同名Action(方法)的verb必须不同.
最后在ReflectedControllerDescriptor.FindAction(…)方法中将得到的MethodInfo对象包装为一个ReflectedActionDescriptor的实例对象并返回。
3. 查找Controller和Action的声明的所有Attribute
FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);
调用GetFilters(…)方法找出Controller和Action声明的attribute,并将这些attribute存入FilterInfo对象对应的List中。
protected virtual FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
FilterInfo filters = actionDescriptor.GetFilters();
// if the current controller implements one of the filter interfaces, it should be added to the list at position 0
ControllerBase controller = controllerContext.Controller;
AddControllerToFilterList(controller, filters.ActionFilters);
AddControllerToFilterList(controller, filters.ResultFilters);
AddControllerToFilterList(controller, filters.AuthorizationFilters);
AddControllerToFilterList(controller, filters.ExceptionFilters);
return filters;
}调用ReflectedActionDiscriptor.GetFilters()方法返回FilterInfo对象,这个FilterInfo包含所有在Controller和Action上声明的attribute。看一下AddControllerToFilterList(…)方法的代码
private static void AddControllerToFilterList<TFilter>(ControllerBase controller, IList<TFilter> filterList) where TFilter : class {
TFilter controllerAsFilter = controller as TFilter;
if (controllerAsFilter != null) {
filterList.Insert(0, controllerAsFilter);
}
}FilterInfo有四个IList<T>类型的属性,分别是 IList<IActionFilter> , IList<IAuthorizationFilter>, IList<IExceptionFilter>和IList<IResultFilter>,上面的代码在做这样一件事:如果Controller能被转换为TFilter(IActionFilter, IAuthorizationFilter, IExceptionFilter, IResultFilter)类型的话,就把这个Controller插入到对应的FilterInfo.ActionFilters或FilterInfo.ResultFilters或FilterInfo.AuthorizationFilters或FilterInfo.ExceptionFilters中。由于Controller继承了IActionFilter, IAuthorizationFilter, IExceptionFilter, IResultFilter这四个接口,所以controller一定可以转换为TFilter类型,也是中说Controller必定被插入到上述4个List集合的第一个位置,为什么是第一个位置,这么做是有用意的。
4. 获取Action需要的参数
IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);
GetParameterValues(…)方法的具体代码:
protected virtual IDictionary<string, object> GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {调用ReflectedActionDescriptor.GetParameters()方返回Parameterescriptor[]数组,并在下面的foreach中对每一个参数列表中的参数赋值。GetParameterValue(controllerContext, parameterDescriptor)方法的逻辑会用一篇单独的文字来说明。actionDescriptor.GetParameters()方法执行了哪些操作:
Dictionary<string, object> parametersDict = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
ParameterDescriptor[] parameterDescriptors = actionDescriptor.GetParameters();
foreach (ParameterDescriptor parameterDescriptor in parameterDescriptors) {
parametersDict[parameterDescriptor.ParameterName] = GetParameterValue(controllerContext, parameterDescriptor);
}
return parametersDict;
}public override ParameterDescriptor[] GetParameters() {又去调用LazilyFetchParametersController() 方法
ParameterDescriptor[] parameters = LazilyFetchParametersCollection();
// need to clone array so that user modifications aren't accidentally stored
return (ParameterDescriptor[])parameters.Clone();
}private ParameterDescriptor[] LazilyFetchParametersCollection() {DescriptorUitl.LazilyFetchOrCreateDescriptors<ParameterInfo, ParameterDescriptor>(…)才是最终要返回ParameterDescriptor[]的地方
return DescriptorUtil.LazilyFetchOrCreateDescriptors<ParameterInfo, ParameterDescriptor>(
ref _parametersCache /* cacheLocation */,
MethodInfo.GetParameters /* initializer */,
parameterInfo => new ReflectedParameterDescriptor(parameterInfo, this) /* converter */);
}public static TDescriptor[] LazilyFetchOrCreateDescriptors<TReflection, TDescriptor>(ref TDescriptor[] cacheLocation, Func<TReflection[]> initializer, Func<TReflection, TDescriptor> converter) {
// did we already calculate this once?
TDescriptor[] existingCache = Interlocked.CompareExchange(ref cacheLocation, null, null);
if (existingCache != null) {
return existingCache;
}
TReflection[] memberInfos = initializer();
TDescriptor[] descriptors = memberInfos.Select(converter).Where(descriptor => descriptor != null).ToArray();
TDescriptor[] updatedCache = Interlocked.CompareExchange(ref cacheLocation, descriptors, null);
return updatedCache ?? descriptors;
}这里使用了一个原子级的比较赋值操作Interlocked.CompareExchange(ref cacheLocation, null, null)。如果cacheLocation == null(第3个参数), 那么把第2个参数的值赋给第1个参数,但这个方法的返回值永远是第一个参数的原始值。
5. 执行Action
ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);
InvokeActionMethodWithFilters(…)方法的执行很巧,利用闭包来达到一个循环的目地:protected virtual ActionExecutedContext InvokeActionMethodWithFilters(ControllerContext controllerContext, IList<IActionFilter> filters, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters) {
ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters);
Func<ActionExecutedContext> continuation = () =>
new ActionExecutedContext(controllerContext, actionDescriptor, false /* canceled */, null /* exception */) {
Result = InvokeActionMethod(controllerContext, actionDescriptor, parameters)
};
// need to reverse the filter list because the continuations are built up backward
Func<ActionExecutedContext> thunk = filters.Reverse().Aggregate(continuation,
(next, filter) => () => InvokeActionMethodFilter(filter, preContext, next));
return thunk();
}这里的重点是thunk这个变量,首先filters变量是一个以Controller为首元素的List<IActionFilter>集合,其中的元素顺序是Controller->IActionFilter, 执行了filters.Reverse()方法后元素顺序被反转为IActionFilter->Controller。Aggregate(continuation, (next, filter))是一个聚合函数,在上面的代码中它作用在filters.Revenser()返回的IEnumerable<IActonFilter>对象上,在Aggregate(continuation, (next, filter))这个签名中, (next, filter)中的next就是continuation对象, filter是IEnumerable<IActionFilter>中的元素;整个Aggregate(…)方法将返回值是累加后的continuation对象。
奇妙的事表就发生在这里:(next, filter) => () => InvokeActionMethodFilter(filter, preContext, next)),
- 当第一次执行Aggregate(…)这个聚合函数时,匿名方法(next, filter)中的next就是continuation而filter是ActionFilter对象,它将InvokeActionMethodFilter(filter, preContext, next)做为返回值并做为下一次执行(next, filter)中的next参数
- 第二次执行匿名方法时next参数的值是InvokeActionMethodFilter(filter, preContext, next), filter是Controller, 最终将InvokeActionMethodFilter(filter, preContext, next))做为结果返回给thunk变量。
上述整个表达式为:thunk = InvokeActionMethodFilter(controller, preContext, InvokeActionMethodFilter(ActionFilter, preContext, continuation) )
经过上述步骤后thunk形成一个链表,其最先执行的还是Controller,这也是为什么在AddControllerToFilterList方法中将Contrtoller做为第一个元素的原因。InvokeActionMethodFilter方法的源码如下:
internal static ActionExecutedContext InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func<ActionExecutedContext> continuation) {
filter.OnActionExecuting(preContext);
if (preContext.Result != null) {
return new ActionExecutedContext(preContext, preContext.ActionDescriptor, true /* canceled */, null /* exception */) {
Result = preContext.Result
};
}
bool wasError = false;
ActionExecutedContext postContext = null;
try {
postContext = continuation();
}
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.
postContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false /* canceled */, null /* exception */);
filter.OnActionExecuted(postContext);
throw;
}
catch (Exception ex) {
wasError = true;
postContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false /* canceled */, ex);
filter.OnActionExecuted(postContext);
if (!postContext.ExceptionHandled) {
throw;
}
}
if (!wasError) {
filter.OnActionExecuted(postContext);
}
return postContext;
}
- 当InvokeActionMethodFilter执行到postContext = continuation(),程序将跳转到InvokeActionMethodFilter(filter, preContext, next))去执行,方法具体签名InvokeActionMethodFilter(continuation, preContext, IActionFilter))
- 再次运行到postContext = continuation(),此时的continuation就是下面匿名方法
() =>
new ActionExecutedContext(controllerContext, actionDescriptor, false /* canceled */, null /* exception */) {
Result = InvokeActionMethod(controllerContext, actionDescriptor, parameters)
};在这个匿名方法中创建一个ActionExecutedContext对象,并将InvokeActionMethod的执行结果赋予Result属性。
InvokeActionMethod(…)的源码:
protected virtual ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters) {
object returnValue = actionDescriptor.Execute(controllerContext, parameters);
ActionResult result = CreateActionResult(controllerContext, actionDescriptor, returnValue);
return result;
}ReflectedActionDescriptor.Execute(…)方法返回的就是ActionResult,调用CreateActionResult(…)只是进行类型转换, 将returnValue转换为ActionResult。
ReflectedActionDescriptor.Execute(…)方法的源码如下:
public override object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters) {
if (controllerContext == null) {
throw new ArgumentNullException("controllerContext");
}
if (parameters == null) {
throw new ArgumentNullException("parameters");
}
ParameterInfo[] parameterInfos = MethodInfo.GetParameters();
var rawParameterValues = from parameterInfo in parameterInfos
select ExtractParameterFromDictionary(parameterInfo, parameters, MethodInfo);
object[] parametersArray = rawParameterValues.ToArray();
ActionMethodDispatcher dispatcher = DispatcherCache.GetDispatcher(MethodInfo);
object actionReturnValue = dispatcher.Execute(controllerContext.Controller, parametersArray);
return actionReturnValue;
}DispatcherCache.GetDispatcher(MethodInfo):以MethodInfo为Key,从缓存中取出ActionMethodDispatcher。
在ActionMethodDispatcher 类的构造函数中调用GetExecutor(…)方法生成1个以controller和action的参数为参数的ExpressionTree,所以执行ActionmethodDispatcher.Execute(…)方法就要传递这两个类型的参数,并返回这个Acton生成的结果,即ActionResult。
6. 呈现ActionResult到客户端
InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result);
InvokeActionResultWithFilters(…)方法做了两个工作:1. 执行IResultFilter 2. 将ActionResult呈现到客户端。 InvokeActionResultWithFilters(…)的具体代码:protected virtual ResultExecutedContext InvokeActionResultWithFilters(ControllerContext controllerContext, IList<IResultFilter> filters, ActionResult actionResult) {它的执行流程与InvokeActionMethodWithFilters(…)方法大同小异,这里的关键方法是InvokeActionResult(controllerContext, actionResult);
ResultExecutingContext preContext = new ResultExecutingContext(controllerContext, actionResult);
Func<ResultExecutedContext> continuation = delegate {
InvokeActionResult(controllerContext, actionResult);
return new ResultExecutedContext(controllerContext, actionResult, false /* canceled */, null /* exception */);
};
// need to reverse the filter list because the continuations are built up backward
Func<ResultExecutedContext> thunk = filters.Reverse().Aggregate(continuation,
(next, filter) => () => InvokeActionResultFilter(filter, preContext, next));
return thunk();
}protected virtual void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) {
actionResult.ExecuteResult(controllerContext);
}调用ActionResult.ExecuteResult(…)方法, 这个ActionResult对象其实是Action返回的具体ActionResult类型,比如:ViewResult, ContentResult, EmptyResult, FileResult等。
总结: 通过对ActionInvoker.InvokeAction(…)的学习让我学到了下面的东西
- ReaderWriterLock类的AcquireReaderLock和AcquireWriterLock, 经典的使用场景莫过于同步的读写操作
- Interlocked.CompareExchange, 这里一个原子级的比较、赋值操作,是一个线程安全与高效的方法
下面是Controller.Execute()执行的时序图: