前面我们介绍了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提供体系的更多相关文章

  1. ASP.NET MVC5学习笔记之Filter基本介绍

    Filter是ASP.NET MVC框架提供的基于AOP(面向方面)设计,提供在Action执行前后做一些非业务逻辑通用处理,如用户验证,缓存等.现在来看看Filter相关的一些类型信息. 一.基本类 ...

  2. ASP.NET MVC5学习笔记之Action参数模型绑定值提供体系

    这一节我们关注模型绑定的值提供体系,先来介绍几个重要的接口 一. IValueProvider,接口定义如下: public interface IValueProvider { bool Conta ...

  3. ASP.NET MVC5学习笔记01

    由于之前在项目中也使用MVC进行开发,但是具体是那个版本就不是很清楚了,但是我觉得大体的思想是相同的,只是版本高的在版本低的基础上增加了一些更加方便操作的东西.下面是我学习ASP.NET MVC5高级 ...

  4. ASP.NET MVC5学习笔记之Controller同步执行架构分析

    在开始之前,声明一下,由于ASP.NET MVC5正式发布了,后面的分析将基于ASP.NET MVC5最新的源代码.在前面的内容我们分析了怎样根据路由信息来确定Controller的类型,并最终生成C ...

  5. ASP.NET MVC5 学习笔记-1 控制器、路由、返回类型、选择器、过滤器

    [TOC] 1. Action 1.1 新建项目 新建项目->Web->Asp.net Web应用程序,选择MVC,选择添加测试. 在解决方案上右键,选择"管理NuGet程序包& ...

  6. ASP.NET MVC5学习笔记之Action参数模型绑定之模型元数据和元数据提供

    一. 元数据描述类型ModelMetadata 模型元数据是对Model的描述信息,在ASP.NET MVC框架中有非常重要的作用,在模型绑定,模型验证,模型呈现等许多地方都有它的身影.描述Model ...

  7. ASP.NET MVC5 学习笔记-2 Razor

    1. Razor @*注释*@ 你在用 @Request.Browser.Browser, 发送邮件给support@qq.com, 转义@@qq @{ var amounts = new List& ...

  8. ASP.NET MVC5学习笔记之Action参数模型绑定基本过程

    当我们在Controller中定义一个Action,通常会定义一个或多个参数,每个参数称为一个模型,ASP.NET MVC框架提供了一种机制称为模型绑定,会尝试自动从请求的信息中实例化每一个模型并赋值 ...

  9. ASP.NET MVC5学习笔记之Controller执行ControllerDescriptor和ActionDescriptor

    一. ControllerDescriptor说明 ControllerDescriptor是一个抽象类,它定义的接口代码如下: public abstract class ControllerDes ...

随机推荐

  1. 剑指Offer:面试题3——二维数组中的查找(java实现)

    问题描述:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 思路:取数组中的元素与 ...

  2. 使用Spring的Property文件存储测试数据 - 初始化

    本系列博客有一个前提:只使用Junit编写测试,不使用类似Cucumber这类BDD框架. 用Cucumber的时候,测试数据可以直接写在feature文件里,但是仅仅使用Junit(不要问我为什么只 ...

  3. Fiddler抓包工具的使用

    下载 自行去官网下载 http://www.telerik.com/fiddler 配置Fiddler 1.打开Fiddler, Tools-> Fiddler Options -> HT ...

  4. flex的Cairngorm框架

    由于要写flex的项目,接触了一段时间的Cairngorm框架,初步认识它是flex的一个mvc结构的框架实现了页面,调用相应方法的控制,和后台交互之间的三层之间的联系.Cairngorm框架主要包括 ...

  5. OpenStack和Redis

    前言: 最近开始捣鼓OpenStack了,在用RDO部署OpenStack的时候,发现装了Redis, 遂决定看看OpenStack哪些地方(可以)用到Redis.  Redis作为OpenStack ...

  6. telnet localhost 8089 ==》》命令使用

    GET /ccc/abc.html HTTP/1.1 host:localhost     客户端连上web服务器后,若想获得web服务器中的某个web资源,需遵守一定的通讯格式, HTTP协议用于定 ...

  7. android Tab =viewpager+fragmnet

    1.定义几个fragment 的subclass 如fragmentone,fragmenttwo; public class fragmentthree extends Fragment { pri ...

  8. centos7 搭建docker内运行rabbitmq,然后再镜像ha方案的完全教程,暂时一个宿主机只能运行一个docker的rabbitmq,但是集群 ha都正常

    1.安装centos7.x,配置好网络2.因为docker需要比较高版本的内核,比如使用overlayfs作为默认docker文件系统要3.18,所以先升级内核到3.18以上版本,能直接过4是最佳了检 ...

  9. poj1942 Paths on a Grid

    处理阶乘有三种办法:(1)传统意义上的直接递归,n的规模最多到20+,太小了,在本题不适用,而且非常慢(2)稍快一点的算法,就是利用log()化乘为加,n的规模虽然扩展到1000+,但是由于要用三重循 ...

  10. 远程DLL注入

    界面如下: 关键部分代码如下: void CInjectDllDlg::OnBnClickedButtonInject() { // TODO: 在此添加控件通知处理程序代码 UpdateData(T ...