asp.net mvc源码分析-Action篇 Filter
紧接着上篇 asp.net mvc源码分析-Controllerl篇 ControllerDescriptor 现在我们该看 FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);这句代码了,意思很好明白就是获取当前的FilterInfo信息,而该方法非常简单就一句return new FilterInfo(_getFiltersThunk(controllerContext, actionDescriptor));
首先我们来看看_getFiltersThunk是个上面东西:
private Func<ControllerContext, ActionDescriptor, IEnumerable<Filter>> _getFiltersThunk = (cc, ad) => FilterProviders.Providers.GetFilters(cc, ad);
意思是根据当前的ControllerContext和ActionDescriptor来获取所有的Filter实例,这里提到一个 FilterProviders.Providers东东,
public static class FilterProviders {
static FilterProviders() {
Providers = new FilterProviderCollection();
Providers.Add(GlobalFilters.Filters);
Providers.Add(new FilterAttributeFilterProvider());
Providers.Add(new ControllerInstanceFilterProvider());
}
public static FilterProviderCollection Providers {
get;
private set;
}
}
看看mvc默认就提供了这3个FilterProvider,GlobalFilters.Filters没什么说,意思就是注册全局的Filiter处理,在我们默认的Global.asax.cs的Application_Start()中有这个一句
RegisterGlobalFilters(GlobalFilters.Filters);所以GlobalFilters.Filters很好明白,接下来我们看看 Providers.Add(new ControllerInstanceFilterProvider())
public class ControllerInstanceFilterProvider : IFilterProvider {
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
if (controllerContext.Controller != null) {
// Use FilterScope.First and Order of Int32.MinValue to ensure controller instance methods always run first
yield return new Filter(controllerContext.Controller, FilterScope.First, Int32.MinValue);
}
}
}
这个ControllerInstanceFilterProvider 非常特殊,我们可以不用管,之所以会有这个东东,是因为Controller实现了 IActionFilter,IAuthorizationFilter,IExceptionFilter,IResultFilter,如果没有这个ControllerInstanceFilterProvider 那么Controller实现这些接口就没有意义了,因为相应的方法没法调用。
而FilterAttributeFilterProvider的实现相对比较复杂一点,但是很好理解,主要获取当前Controller和Action的FilterAttribute实例,还记得上篇而文章提到的ReflectedAttributeCache里面有ConcurrentDictionary<MethodInfo, ReadOnlyCollection<FilterAttribute>> _methodFilterAttributeCache和ConcurrentDictionary<Type, ReadOnlyCollection<FilterAttribute>> _typeFilterAttributeCache这两个东西吗?
现在我们来看看Filter的构造函数,里面有这么几句需要我们注意
if (order == null) {
IMvcFilter mvcFilter = instance as IMvcFilter;
if (mvcFilter != null) {
order = mvcFilter.Order;
}
}
Order = order ?? DefaultOrder;
Scope = scope;
当我们的Filter是通过ControllerInstanceFilterProvider来创建的话它的order不为null,所以我们不用考虑,而Scope =Global
如果Filter是用过GlobalFilters.Filters来创建的话,那就看在添加是是否有order,没有order的话,就看当前实例是否实现了IMvcFilter,实现了的话就是接口的order,没有则是-1;而Scope =First
如果是通过FilterAttributeFilterProvider来创建的话,它进来的order是null,但是FilterAttribute是实现了IMvcFilter接口的,所以它的order是默认是Filter.DefaultOrder (-1),而scope是Controller和Action,主要看属性是在Controller上还是在Action上。
在这些Filter集合中,是有一定的顺序的,先按照order从小到大排序,如果order相同就按Scope从小到大排序,最后去掉重复的。
现在我们来看看FilterInfo的够着函数了:
public FilterInfo(IEnumerable<Filter> filters) {
// evaluate the 'filters' enumerable only once since the operation can be quite expensive
var filterInstances = filters.Select(f => f.Instance).ToList();
_actionFilters.AddRange(filterInstances.OfType<IActionFilter>());
_authorizationFilters.AddRange(filterInstances.OfType<IAuthorizationFilter>());
_exceptionFilters.AddRange(filterInstances.OfType<IExceptionFilter>());
_resultFilters.AddRange(filterInstances.OfType<IResultFilter>());
}
根据当前的filiter集合,把它们分别放到不同filter接口集合中。
下面 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);
}
这几句就很好明白了,对此次请求做身份验证,如果没有通过则返回一个认证失败。这个东西很重要尤其是在权限设计的时候。
验证 成功后继续执行
if (controllerContext.Controller.ValidateRequest) {
ValidateRequest(controllerContext);
}
Controller.ValidateRequest默认值为true,即是需要验证客户端请求的,
if (controllerContext.IsChildAction) {return;}
ValidationUtility.EnableDynamicValidation(HttpContext.Current);
controllerContext.HttpContext.Request.ValidateInput();
具体的验证内容很多,很麻烦,这里就忽略它吧。
说了这么多我们来总结一下吧:如果我们要给整个应用程序添加一些处理,可以通过GlobalFilters.Filters来添加,如果需要给某个Controller类或则是Action添加特殊处理,可以通过FilterAttributeFilterProvider来添加。在我们实际开发中往往用到的是ActionFilterAttribute
public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IResultFilter
当然如果有特殊的需求我们可以添加自己的FilterProvider,如在Application_Start()中可以写成 FilterProviders.Providers.Add(xxxxx);
接下来我我们看看这些Filter事这么调用的,凭我们的直觉调用顺序应该是
OnActionExecuting
Action ......真正调用Action
OnActionExecuted
OnResultExecuting
Result....真正调用Result
OnResultExecuted
而剩下的代码就只有这
IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);
ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);
InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result);几句了,
IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);这个方法是用来获取Action参数对应的值,实现很复杂我们计划放到后面在将。
看到InvokeActionMethodWithFilters这个方法我们就知道实现的功能是调用Action和IActionFilter的两个方法:
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(); } |
这段代码我看了很久才看懂它的细节。 ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters);这句就没什么说的了,简单实例化一个ActionExecutingContext对象,
Func<ActionExecutedContext> continuation = () =>
new ActionExecutedContext(controllerContext, actionDescriptor, false /* canceled */, null /* exception */) {
Result = InvokeActionMethod(controllerContext, actionDescriptor, parameters)
};
这句有个方法continuation 没有输入参数返回ActionExecutedContext实例,只是这个实例有一个Result 属性,它的之就是我们调用Action后的返回值(ActionResult类型)。
Func<ActionExecutedContext> thunk = filters.Reverse().Aggregate(continuation,
(next, filter) => () => InvokeActionMethodFilter(filter, preContext, next));
return thunk();
这里关键是Aggregate的用法,continuation是一个初始值,将每次 InvokeActionMethodFilter(filter, preContext, next));的结果赋给continuation值,InvokeActionResultFilter的代码大致如下:
internal static ResultExecutedContext InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func<ResultExecutedContext> continuation) {
filter.OnResultExecuting(preContext);
bool wasError = false;
ResultExecutedContext postContext = null;
postContext = continuation();
filter.OnResultExecuted(postContext);
return postContext;
}
这里个方法没有修改continuation的值,当filter循环执行 filter.OnResultExecuting(preContext)完了的时候就开始调用continuation方法了,在执行了Action,再次循环filter 以执行 filter.OnResultExecuted(postContext);
总之这段代码写的很难看懂,我个人认为拆开写要好些
foreach(Filiter ,...)
{
filter.OnResultExecuting(preContext);
}
postContext = continuation();
foreach(Filiter ,...)
{
filter.OnResultExecuted(postContext);
}
我当时为了理解这段代码自己写了一个语法测试
class Program { static void Main( string [] args) { List< string > users = new List< string > { "majiang" , "luyang" , "xieyichuan" }; Func< string > seed = () => "First" ; Func<Func< string >, string , Func< string >> next = (fun, str) => delegate () { Console.WriteLine(str); var temp = fun(); temp += "-" + str; Console.WriteLine(temp); return temp; }; Func< string > result = users.Aggregate(seed, next); result(); Console.ReadLine(); } } static class Helper { public static TAccumulate Aggregate<TSource, TAccumulate>( this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func) { TAccumulate local = seed; foreach (TSource local2 in source) { local = func(local, local2); } return local; } } |
剩下的InvokeActionResultWithFilters方法和这里的InvokeActionMethodWithFilters方法调用一样
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构