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 ...
随机推荐
- checkbox与文字的间距
1. checkbox在更换了图片后, 与文字的距离有问题, 建议修改background为@null, 去除占据的位置. 2. checkbox的paddingleft可以控制图片和文字的间距.
- 阿里RDS备份恢复
未使用root用户操作: 数据库版本要一致 数据目录:/data/mysqlbak/ 先按阿里给的步骤操作,最后出现一步出现,无法找到back_xxx.conf,但该文件已经存在.解决方法: sudo ...
- A手机等的网络udp广播,收到广播以后回复udp消息
B手机:向A手机发送一条消息,等待A回复 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: ...
- WEB前端研发工程师编程能力成长之路(1)(转)
WEB前端研发工程师编程能力成长之路(1) [背景] 如果你是刚进入WEB前端研发领域,想试试这潭水有多深,看这篇文章吧: 如果你是做了两三年WEB产品前端研发,迷茫找不着提高之路,看这篇文章吧: ...
- git从入门到精通
1.git简介 由Linus在2005年花了两周时间完成,也就是开发Linux系统的大神研制. git特点: 自由和开放源码 隐形备份 安全 不需要强大的硬件 更简单的分支 对文件操作容易 2.文件操 ...
- MongoDB 1: NoSQL 和 SQL的区别
导读:本篇博客,主要是结合自己在项目中的使用,简单的阐述一下NoSQL和SQL的区别.那么,根据自己的应用,NoSQL这边,选择的是MongoDB(Redis虽然也是,但属于内存存储,这里不予说明). ...
- pyrrd 程序
http://elekslabs.com/2013/12/rrd-and-rrdtool-sar-graphs-using-pyrrd.html http://thepiandi.blogspot.j ...
- 【ILSpy反编译】C# 写的程序反编译查看是不是也太容易了点吧,太恐怖了。。。
最近由于要写一些界面的东西,写了几个月c#(之前一直做c/c++项目),发现c#写界面很方便,效果也不错,在这个过程中也听说c#程序可以很容易被反编译到,但一直也没时间去自己反编译去试着看看,心想就算 ...
- C#常用的正则表达式
using System.Text.RegularExpressions; 英文Regex.IsMatch("字符串", @"^[a-zA-Z0-9_\u4e00-\u9 ...
- 对C语言中va_list,va_start,va_arg和va_end的一点理解
这几个函数和变量是针对可变参数函数的,什么是可变参数函数呢,最经典的莫过于printf和scanf,这两个函数的声明如下: int printf(const char *format, ...); i ...