ASP.NET MVC5学习笔记之Filter提供体系
前面我们介绍了Filter的基本使用,但各种Filter要在合适的时机运行起来,需要预先准备好,现在看看ASP.NET MVC框架是怎么做的。
一.Filter集合
在ControlerActionInvoker的InvokeAction方法中,只有一行代码FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor), 把收集的Filter信息放到了FilterInfo中,我们来看看
访类型的定义:
public class FilterInfo
{ public FilterInfo(); public FilterInfo(IEnumerable<Filter> filters); public IList<IActionFilter> ActionFilters { get; }
public IList<System.Web.Mvc.Filters.IAuthenticationFilter> AuthenticationFilters { get; } public IList<IAuthorizationFilter> AuthorizationFilters { get; } public IList<IExceptionFilter> ExceptionFilters { get; } public IList<IResultFilter> ResultFilters { get; }
}
可以看到,这个类型定义的5种系统Filter集合信息. 来看看它带参的构造函数:
public FilterInfo(IEnumerable<Filter> filters)
{
// Determine the override scope for each filter type and cache the filters list.
OverrideFilterInfo processed = ProcessOverrideFilters(filters);
// Split the cached filters list based on filter type and override scope.
SplitFilters(processed);
}
可以看到分两步:
1 .ProcessOverrideFilters处理前面的提到IOverrideFilter接口
2. 从列表中把Filter分离到各自的Filter接口集合
ProcessOverrideFilters方法的代码如下:
private static OverrideFilterInfo ProcessOverrideFilters(IEnumerable<Filter> filters)
{
OverrideFilterInfo result = new OverrideFilterInfo
{
ActionOverrideScope = FilterScope.First,
AuthenticationOverrideScope = FilterScope.First,
AuthorizationOverrideScope = FilterScope.First,
ExceptionOverrideScope = FilterScope.First,
ResultOverrideScope = FilterScope.First,
Filters = new List<Filter>()
}; // Evaluate the 'filters' enumerable only once since the operation can be quite expensive.
foreach (Filter filter in filters)
{
if (filter == null)
{
continue;
}
IOverrideFilter overrideFilter = filter.Instance as IOverrideFilter; if (overrideFilter != null)
{
if (overrideFilter.FiltersToOverride == typeof(IActionFilter)
&& filter.Scope >= result.ActionOverrideScope)
{
result.ActionOverrideScope = filter.Scope;
}
else if (overrideFilter.FiltersToOverride == typeof(IAuthenticationFilter)
&& filter.Scope >= result.AuthenticationOverrideScope)
{
result.AuthenticationOverrideScope = filter.Scope;
}
else if (overrideFilter.FiltersToOverride == typeof(IAuthorizationFilter)
&& filter.Scope >= result.AuthorizationOverrideScope)
{
result.AuthorizationOverrideScope = filter.Scope;
}
else if (overrideFilter.FiltersToOverride == typeof(IExceptionFilter)
&& filter.Scope >= result.ExceptionOverrideScope)
{
result.ExceptionOverrideScope = filter.Scope;
}
else if (overrideFilter.FiltersToOverride == typeof(IResultFilter)
&& filter.Scope >= result.ResultOverrideScope)
{
result.ResultOverrideScope = filter.Scope;
}
} // Cache filters to avoid having to enumerate it again (expensive). Do so here to avoid an extra loop.
result.Filters.Add(filter);
} return result;
}
这段代码遍历Filter列表,记录实现了IOverrideFilter的Filter的最高OverrideScope, 在下面的SplitFilters处理中,少于OverrideScope的Filter将不会添加到最终的集合中
SplitFilters方法代码如下:
private void SplitFilters(OverrideFilterInfo info)
{
Contract.Assert(info.Filters != null); foreach (Filter filter in info.Filters)
{
Contract.Assert(filter != null); IActionFilter actionFilter = filter.Instance as IActionFilter; if (actionFilter != null && filter.Scope >= info.ActionOverrideScope)
{
_actionFilters.Add(actionFilter);
} IAuthenticationFilter authenticationFilter = filter.Instance as IAuthenticationFilter; if (authenticationFilter != null && filter.Scope >= info.AuthenticationOverrideScope)
{
_authenticationFilters.Add(authenticationFilter);
} IAuthorizationFilter authorizationFilter = filter.Instance as IAuthorizationFilter; if (authorizationFilter != null && filter.Scope >= info.AuthorizationOverrideScope)
{
_authorizationFilters.Add(authorizationFilter);
} IExceptionFilter exceptionFilter = filter.Instance as IExceptionFilter; if (exceptionFilter != null && filter.Scope >= info.ExceptionOverrideScope)
{
_exceptionFilters.Add(exceptionFilter);
} IResultFilter resultFilter = filter.Instance as IResultFilter; if (resultFilter != null && filter.Scope >= info.ResultOverrideScope)
{
_resultFilters.Add(resultFilter);
}
}
}
实现各个Filter的分离,代码很简单,不再说明。
二. Filter收集
在ASP.NET MVC5中最终通过FilterProviders.Providers.GetFilters方法得到所有的Filter列表,我们先来看看FilterProviders这个类型,定义如下:
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; }
}
从中我们可以看到系统定义了三个FilterProvider,它们都实现了IFilterProvider接口,该接口定义如下:
public interface IFilterProvider
{
IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
}
现在来看看这几个Provider:
a. GlobalFilters.Filters
这个故名思义是收集全局范围运行的Filter,代码如下:
public static class GlobalFilters
{
static GlobalFilters()
{
Filters = new GlobalFilterCollection();
} public static GlobalFilterCollection Filters { get; private set; }
}
比如通常的项目模板在App_Start的FilterConfig中有如下代码,添加全局出错处理
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
b. FilterAttributeFilterProvider
该类型是帮助收集应用在Controller和Action上的Filter,代码如下:
public class FilterAttributeFilterProvider : IFilterProvider
{
private readonly bool _cacheAttributeInstances; public FilterAttributeFilterProvider()
: this(true)
{
} public FilterAttributeFilterProvider(bool cacheAttributeInstances)
{
_cacheAttributeInstances = cacheAttributeInstances;
} protected virtual IEnumerable<FilterAttribute> GetActionAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
return actionDescriptor.GetFilterAttributes(_cacheAttributeInstances);
} protected virtual IEnumerable<FilterAttribute> GetControllerAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
return actionDescriptor.ControllerDescriptor.GetFilterAttributes(_cacheAttributeInstances);
} public virtual IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
// Results are low in number in the common case so use yield return to avoid creating intermediate collections or nested enumerables
if (controllerContext.Controller != null)
{
foreach (FilterAttribute attr in GetControllerAttributes(controllerContext, actionDescriptor))
{
yield return new Filter(attr, FilterScope.Controller, order: null);
}
foreach (FilterAttribute attr in GetActionAttributes(controllerContext, actionDescriptor))
{
yield return new Filter(attr, FilterScope.Action, order: null);
}
}
}
}
c. ControllerInstanceFilterProvider
controller本身也实现了一些Filter接口,通过该Provider加入,代码如下:
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);
}
}
}
接下来看一看FilterProviderCollection的GetFilters实现:
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
//省略检查代码
IFilterProvider[] providers = CombinedItems;
List<Filter> filters = new List<Filter>();
for (int i = ; i < providers.Length; i++)
{
IFilterProvider provider = providers[i];
foreach (Filter filter in provider.GetFilters(controllerContext, actionDescriptor))
{
filters.Add(filter);
}
} filters.Sort(_filterComparer); if (filters.Count > )
{
RemoveDuplicates(filters);
}
return filters;
}
从中我们可以看到,主要分为三步:
1. 通过FilterProviders 把Filter收集到一个Filter列表
2. 对列表进行排序,排序规则是根据Order和Scope
3. 列表去重
FilterComparer是Filter排序比较器,代码如下, 从中我们可以看到Order 和 Scope是怎么影响排序顺序
private class FilterComparer : IComparer<Filter>
{
public int Compare(Filter x, Filter y)
{
// Nulls always have to be less than non-nulls
if (x == null && y == null)
{
return ;
}
if (x == null)
{
return -;
}
if (y == null)
{
return ;
} // Sort first by order... if (x.Order < y.Order)
{
return -;
}
if (x.Order > y.Order)
{
return ;
} // ...then by scope if (x.Scope < y.Scope)
{
return -;
}
if (x.Scope > y.Scope)
{
return ;
} return ;
}
}
最后返回列表传递给FilterInfo类型,FilterInfo内部处理见上面.
ASP.NET MVC5学习笔记之Filter提供体系的更多相关文章
- ASP.NET MVC5学习笔记之Filter基本介绍
Filter是ASP.NET MVC框架提供的基于AOP(面向方面)设计,提供在Action执行前后做一些非业务逻辑通用处理,如用户验证,缓存等.现在来看看Filter相关的一些类型信息. 一.基本类 ...
- ASP.NET MVC5学习笔记之Action参数模型绑定值提供体系
这一节我们关注模型绑定的值提供体系,先来介绍几个重要的接口 一. IValueProvider,接口定义如下: public interface IValueProvider { bool Conta ...
- ASP.NET MVC5学习笔记01
由于之前在项目中也使用MVC进行开发,但是具体是那个版本就不是很清楚了,但是我觉得大体的思想是相同的,只是版本高的在版本低的基础上增加了一些更加方便操作的东西.下面是我学习ASP.NET MVC5高级 ...
- ASP.NET MVC5学习笔记之Controller同步执行架构分析
在开始之前,声明一下,由于ASP.NET MVC5正式发布了,后面的分析将基于ASP.NET MVC5最新的源代码.在前面的内容我们分析了怎样根据路由信息来确定Controller的类型,并最终生成C ...
- ASP.NET MVC5 学习笔记-1 控制器、路由、返回类型、选择器、过滤器
[TOC] 1. Action 1.1 新建项目 新建项目->Web->Asp.net Web应用程序,选择MVC,选择添加测试. 在解决方案上右键,选择"管理NuGet程序包& ...
- ASP.NET MVC5学习笔记之Action参数模型绑定之模型元数据和元数据提供
一. 元数据描述类型ModelMetadata 模型元数据是对Model的描述信息,在ASP.NET MVC框架中有非常重要的作用,在模型绑定,模型验证,模型呈现等许多地方都有它的身影.描述Model ...
- ASP.NET MVC5 学习笔记-2 Razor
1. Razor @*注释*@ 你在用 @Request.Browser.Browser, 发送邮件给support@qq.com, 转义@@qq @{ var amounts = new List& ...
- ASP.NET MVC5学习笔记之Action参数模型绑定基本过程
当我们在Controller中定义一个Action,通常会定义一个或多个参数,每个参数称为一个模型,ASP.NET MVC框架提供了一种机制称为模型绑定,会尝试自动从请求的信息中实例化每一个模型并赋值 ...
- ASP.NET MVC5学习笔记之Controller执行ControllerDescriptor和ActionDescriptor
一. ControllerDescriptor说明 ControllerDescriptor是一个抽象类,它定义的接口代码如下: public abstract class ControllerDes ...
随机推荐
- poj 2299 Ultra-QuickSort :归并排序求逆序数
点击打开链接 Ultra-QuickSort Time Limit: 7000MS Memory Limit: 65536K Total Submissions: 34676 Accepted ...
- NYOJ 123 士兵杀敌4-树状数组的插线求点
士兵杀敌(四) 时间限制:2000 ms | 内存限制:65535 KB 难度:5 描述 南将军麾下有百万精兵,现已知共有M个士兵,编号为1~M,每次有任务的时候,总会有一批编号连在一起人请战(编 ...
- java类总结
1.java类与类之间存在六种关系:继承,实现,依赖,关联,聚合组成 2.一个类可以继承另外一个类,并在此基础上添加自己的特有的功能. 3.一个类的方法中操作另外一个类的对象,这种情况称为第一个类依赖 ...
- Unity小厨房之-----背后视角摄像机
相信每一个接触过Unity的人,应该都认识Unity引擎自带的那个水管工,也一定知道那个小人模型上挂着3个脚本控制着小人的一切,今天我就来说一下关于Unity背后视角摄像机的实现,当然方法并不唯一,这 ...
- 在探索中感悟,在摸索中前进--ET之快递法
飞测说:最近很多朋友问如果没有需求文档,我该怎么测试?我自然想起了探索测试,今天来说说自己在探索测试路上的一点点感悟-------快递测试法,让我们在探索中感悟,在摸索中前进-- 快递测试法,是从ET ...
- IIS发布,图片和样式显示不了的问题
今天本地IIS部署在visual stuio 2013里运行成功的一个项目时,出现了样式和图片显示不了的情况,如下图 所有页面的样式和图片不显示,刚开始以为是发布之后的图片和样式的文件夹没有权限,可是 ...
- 翻译「C++ Rvalue References Explained」C++右值引用详解 Part5:右值引用就是右值吗?
本文为第五部分,目录请参阅概述部分:http://www.cnblogs.com/harrywong/p/cpp-rvalue-references-explained-introduction.ht ...
- cocos2d-x Android版游戏之中国移动SDK嵌入
. 拷贝API 将SDK\runtime\CMBilling20007.jar拷贝至游戏工程的runtime目录下(或其他目录) ,但切记不能放在libs目录下编译,否则编译报错(如:bad rang ...
- [mysq]ERROR 2006 (HY000) at line xx: MySQL server has gone away 解决方法
vi /etc/my.cnf wait_timeout=2880000interactive_timeout = 2880000max_allowed_packet = 100M 完整配置文件 [my ...
- spring HibernateValidator 验证 子类不起作用
spring HibernateValidator 验证 子类不起作用,在要验证的子类前加上@Valid即可. public class UserInfo { private int Id; @Val ...