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 ...
随机推荐
- 剑指Offer:面试题3——二维数组中的查找(java实现)
问题描述:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 思路:取数组中的元素与 ...
- 使用Spring的Property文件存储测试数据 - 初始化
本系列博客有一个前提:只使用Junit编写测试,不使用类似Cucumber这类BDD框架. 用Cucumber的时候,测试数据可以直接写在feature文件里,但是仅仅使用Junit(不要问我为什么只 ...
- Fiddler抓包工具的使用
下载 自行去官网下载 http://www.telerik.com/fiddler 配置Fiddler 1.打开Fiddler, Tools-> Fiddler Options -> HT ...
- flex的Cairngorm框架
由于要写flex的项目,接触了一段时间的Cairngorm框架,初步认识它是flex的一个mvc结构的框架实现了页面,调用相应方法的控制,和后台交互之间的三层之间的联系.Cairngorm框架主要包括 ...
- OpenStack和Redis
前言: 最近开始捣鼓OpenStack了,在用RDO部署OpenStack的时候,发现装了Redis, 遂决定看看OpenStack哪些地方(可以)用到Redis. Redis作为OpenStack ...
- telnet localhost 8089 ==》》命令使用
GET /ccc/abc.html HTTP/1.1 host:localhost 客户端连上web服务器后,若想获得web服务器中的某个web资源,需遵守一定的通讯格式, HTTP协议用于定 ...
- android Tab =viewpager+fragmnet
1.定义几个fragment 的subclass 如fragmentone,fragmenttwo; public class fragmentthree extends Fragment { pri ...
- centos7 搭建docker内运行rabbitmq,然后再镜像ha方案的完全教程,暂时一个宿主机只能运行一个docker的rabbitmq,但是集群 ha都正常
1.安装centos7.x,配置好网络2.因为docker需要比较高版本的内核,比如使用overlayfs作为默认docker文件系统要3.18,所以先升级内核到3.18以上版本,能直接过4是最佳了检 ...
- poj1942 Paths on a Grid
处理阶乘有三种办法:(1)传统意义上的直接递归,n的规模最多到20+,太小了,在本题不适用,而且非常慢(2)稍快一点的算法,就是利用log()化乘为加,n的规模虽然扩展到1000+,但是由于要用三重循 ...
- 远程DLL注入
界面如下: 关键部分代码如下: void CInjectDllDlg::OnBnClickedButtonInject() { // TODO: 在此添加控件通知处理程序代码 UpdateData(T ...