ASP.NET MVC:Filter和Action的执行介绍(3)


static void AggTest()
{
int[] data = { 1, 2, 3, 4 };
var res = data.Aggregate("String", (str, val) => str + val.ToString());
Console.WriteLine(res);
}


最后输出的结果是String1234. 回到InvokeActionMethodWithFilters的实现上来,这里对应的类型a是IActionFilter,类型b是Func<ActionExecutedContext>,初始值是continuation。假设我们有3个filter,[f1,f2,f3],我们来看下thunk最终是什么,
第一次: next=continue, filter=f1, 返回值 ()=>InvokeActionMethodFilter(f1, preContext, continue)
第二次:next=()=>InvokeActionMethodFilter(f1, preContext, continue), filter=f2
返回值:()=>InvokeActionMethodFilter(f2, preContext,()=> InvokeActionMethodFilter(f1, preContext, continue)),
最终: thunk= ()=>InvokeActionMethodFilter(f3,preContext,()=>InvokeActionMethodFilter(f2, preContext, ()=>InvokeActionMethodFilter(f1, preContext, continue)));
直到 return thunk()之前,所有真正的代码都没有执行,关键是构建好了thunk这个委托,把thunk展开成上面的样子,应该比较清楚真正的调用顺序什么样的了。这里花了比较多的笔墨介绍了如何通过Aggregate方法构造调用链,这里有一篇文章专门介绍了这个,也可以参考下。想象下,如果filter的功能就是先遍历调用f的Executing方法,然后调用Action方法,最后再依次调用f的Executed方法,那么完全可以用迭代来实现,大可不必如此抽象复杂,关键是ASP.NET MVC对于filter中异常的处理还有一些特殊之处,看下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;
}


代码有点长,首先就是触发了filter的OnActionExecuting方法,这是方法的核心。接下来的重点是 postContext = continuation(); 最后是OnActionExecuted方法,结合上面的展开式,我们可以知道真正的调用顺序将是:

复制代码 代码如下:


f3.Executing->f2.Executing->f1.Exectuing->InvokeActionMethod->f1.Executed->f2->Executed->f3.Executed.


那么,源代码中的注释 // need to reverse the filter list because the continuations are built up backward 的意思也很明了了。需要将filter倒序排一下之后才是正确的执行顺序。
还有一类filter是当异常发生的时候触发的。在InvokeAction方法中可以看到触发它的代码放在一个catch块中。IExceptionFilter的触发流程比较简单,不多做解释了。唯一需要注意的是ExceptionHandled属性设置为true的时候就不会抛出异常了,这个属性在各种context下面都有,他们是的效果是一样的。比如在OnActionExecuted方法中也可以将他设置为true,同样不会抛出异常。这些都比较简单,不再分析其源代码,这篇文章比较详细的介绍了filter流程中出现异常之后的执行顺序。
最后说下Action Method的执行,前面我们已经得到了methodInfo,和通过data binding获得了参数,调用Action Method应该是万事俱备了。asp.net mvc这边的处理还是比较复杂的,ReflectedActionDescriptor会去调用ActionMethodDispatcher的Execute方法,这个方法如下:

复制代码 代码如下:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wjdxxf.html