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 ...
随机推荐
- js定时器、高亮修改单元格背景色
window.setInterval() 功能:按照指定的周期(以毫秒计)来调用函数或计算表达式. 语法:setInterval(code,millisec) 解释:code:在定时时间到时要执行的J ...
- Xcode引入外界文件时选Create groups 或 Create folder references的区别
一.使用Create groups 我们在项目中可以手动添加一个groups(右键点击选择New Group),但是手动添加的groups实际上并不会存在于项目的目录中,被添加进groups中的文件仍 ...
- wmware10安装ghost win7问题处理
随便找到了ghostwin7.iso, 先建立空的虚拟机, 加载iso, 按F2, 设置启动从光盘启动, 启动进去后点直安装Ghost镜像到C盘, 失盘, 直接跳到dos界面了. 忘记先要分区了, 使 ...
- UDKtoUE4Tool-UDKUE3资源移植UE4工具
UDKtoUE4Tool UDKtoUE4Tool 是一个把UE3/UDK资源包(T3D格式)转换成UE4(T3D格式)的工具.作者Matt3D使用C#实现,未来考虑发布到Unreal Marketp ...
- c# 生成json数据包
json数据类型,归根到底就是一个字符串,管他里面什么格式,它就是一个字符串来的! 看一个json数据包: { "touser":"OPENID", " ...
- (原创)robotium自学笔记
按计划6月份之后就要做安卓了,今天抽时间研究了下一款android自动化测试工具rebotium,记录下来备用. 个人感觉还是一个不错的工具. 首先确保已具备android相关环境并且已经创建了安卓模 ...
- cordova 开发属于自己的插件---android
还是需要开发出自己的插件的... 我的cordova version is 4.0.0 1.需要新建一个文件夹为 myplugin 1.1在myplugin文件夹下 新建 plugin.xml文件 ...
- myecplise tomcat jdk
myeclipse是javaweb初学者或者工程师非常常用的软件.那么在MyEclipse中如何使用自己安装的JDK和tomcat呢.下面是JDK1.7+tomcat7.0+myeclipse10的j ...
- 生成ssl证书
http://blog.csdn.net/liuchunming033/article/details/48470575 https://segmentfault.com/a/119000000256 ...
- jQuery插件开发方式
一.jQuery扩展 1.$.extend(object) 类似于.Net的扩展方法,用于扩展jQuery.然后就可以用$.的方式调用. $(function(){ $.extend({ fun1: ...