• 如何使用ASP.NET MVC的Filter


    内容概览

    本篇主要探讨一下mvc一个重要的功能——Filter,我们通过研究源代码来了解Filter的原理,以及AOP 模式和各种Filter的执行。最重要的是大家通过理解Filter的代码,明白Filter的机制,从而对Filter有一个灵活的运用。

        * 强大的Filter
        * 为什么要Filter
        * 获取“贴”在Action上的各种Filter
        * Filter 的执行

    强大的Filter

    在使用asp.net mvc编程中,我想没有人不使用Filter,各种各样的Filter让我们的代码更加简练,功能更加丰富。比如你在Action上使用的每一个 [Attribute]大都是Filter。mvc提供四种类型的 Filter:IActionFilter,IAuthorizationFilter,IExceptionFilter,IResultFilter,这四种Filter足以我们所要实现的功能了,还提供了几个现成的可以使用的Filter:OutputCacheAttribute、 HandleErrorAttribute、AuthorizeAttribute。我们知道Filter是横切在Action上的,所以每次执行一个 Action,就会牵扯到这个Action的执行,不同类型的Filter提供不同类型的操作,Filter的设计就是一种AOP设计模式,所以这种方式十分灵活。

    对于四种类型的Filter,各自有不同的方法,并以此来实现不同的功能,我们可以发挥自己的创造力,定制一个自己的Filter,来完成自己想要的功能。一般的Filter应用的场景有:缓存、验证、异常,以及处理Action上下文等,要想实现自己的Filter 只要继承相应的接口并重写接口的方法就可以了。

    为什么要Filter

    我们知道Filter是一种AOP模式,也就是说它提供我们可以对一系列操作进行横切干扰的手段,而且它解耦了依赖关系。想想这么一种场景,在传统的WebForm中,某一个页面的访问必须去验证当前用户是否符合某一个要求,比如已登录,这时,我们需要获取请求的上下文,分析当前用户的行为状态,从而得到这个条件是否成立,如果我们有很多这种页面,比如所有的后台管理页面需要管理员的登录,我们是不是每一页都写一个这样的验证呢?我们当然不会这么蠢,我们会聪明的把验证的逻辑封装成一个验证类,然后从各个页面调用验证类的验证逻辑,从而得知当前用户是否通过验证。

    这样还是不够好,因为我们不得不在每一处反复写我们的调用验证类的验证代码,显然这些工作是重复的,因为那个时候还没有AOP的概念,所以我们不得不多付出点代价。

    现在有了mvc,它理所应当为我们提供一个AOP的框架,而Filter就是这么一种模式,它的出现容许我们以一种更为简单的方式来实现类似的功能,在使用的时候我们只需要像该某个商品贴标签式的方式给Action“贴”上一个Attribute就行了,一切搞定,没有一点多于的代码。

    获取“贴”在Action上的各种Filter

    还是老套路,我们先寻找第一次出现Filter的地方。在ControllerActionInvoker类的 InvokeAction方法中,我们发现了一些线索:

    1. FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);
    复制代码

    FilterInfo是一个各种Filter是集合类,它有4个private字段和4个public属性:

    1. public IList<IActionFilter> ActionFilters
    2. public IList<IAuthorizationFilter> AuthorizationFilters 
    3. public IList<IExceptionFilter> ExceptionFilters
    4. public IList<IResultFilter> ResultFilters
    复制代码

    FilterInfo是一个没有“技术含量”的类,它的作用就是存放各种类型的Filter,下面看一下GetFilter方法,它有2个参数,一个是 ControllerContext对象,一个是ActionDescriptor对象,这两个对象在上一篇《Action的创建》中详细讨论过了。Go to Defination到GetFilter方法中,我们发现这个方法就是简单的调用了一下ActionDescriptor的GetFilters方法,我们知道AcionDesciptor对象的默认实现是ReflectedActionDescriptor对象,我们找到这个类的GetFilters 方法,下面是它的代码:

    1. public override FilterInfo GetFilters() {
    2.     // Enumerable.OrderBy() is a stable sort, so this method preserves scope ordering.
    3.     ●FilterAttribute[] typeFilters = (FilterAttribute[])MethodInfo.ReflectedType.
    4.                 GetCustomAttributes(typeof(FilterAttribute), true /* inherit */);
    5.     ●FilterAttribute[] methodFilters = (FilterAttribute[])MethodInfo.GetCustomAttributes(
    6.                 typeof(FilterAttribute), true /* inherit */);
    7.     ●List<FilterAttribute> orderedFilters = typeFilters.Concat(methodFilters).OrderBy(
    8.                 attr => attr.Order).ToList();
    9.     ●FilterInfo filterInfo = new FilterInfo();
    10.     MergeFiltersIntoList(orderedFilters, filterInfo.ActionFilters);
    11.     MergeFiltersIntoList(orderedFilters, filterInfo.AuthorizationFilters);
    12.     MergeFiltersIntoList(orderedFilters, filterInfo.ExceptionFilters);
    13.     MergeFiltersIntoList(orderedFilters, filterInfo.ResultFilters);
    14.     return filterInfo;
    15. }
    复制代码

    上面的代码中带有黑点标记的是根据反射得到的Attribute,然后根据它们的Order进行排序。得到一个有序的Filter的集合后,下一步就是对不同类型的Filter进行分类,首先实例化一个FilterInfo对象,然后调用静态方法 MergeFilterIntoList,这个方法也很简单:

    1. private static void MergeFiltersIntoList<TFilter>(
    2.     IList<FilterAttribute> allFilters, 
    3.     IList<TFilter> destFilters) 
    4.     where TFilter : class {
    5.     foreach (FilterAttribute filter in allFilters) {
    6.         TFilter castFilter = filter as TFilter;
    7.         if (castFilter != null) {
    8.             destFilters.Add(castFilter);
    9.         }
    10.     }
    11. }
    复制代码

    该方法有两个参数,一个是所有的Filter,一个是分类后的Filter,由于不同类型的Filter继承了不同的接口,所以不同类型的Filter经过as运算符后,类型相同的装换成功,对象不为null,类型不同的转换后为null,从而对各种不同类型的 Filter进行分类。

    到目前为止,所有的Filter已经获取完毕,但是我们忘了一点,就是Controller本身也是一个 Filter。看一下Controller类是签名就知道了:

    1. public abstract class Controller : ControllerBase, 
    2.     IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter
    复制代码

    在Controller类中有这些接口的virtual方法,我们可能在一个Controller中 override这些方法,那这些Filter是怎么获取的呢?答案在ControllerActionInvoker类的GetFilters方法中:

    1. protected virtual FilterInfo GetFilters(ControllerContext controllerContext, 
    2.             ActionDescriptor actionDescriptor) {
    3.     FilterInfo filters = actionDescriptor.GetFilters();
    4.     // if the current controller implements one of the filter interfaces,
    5.     // it should be added to the list at position 0
    6.     ●ControllerBase controller = controllerContext.Controller;
    7.     ●AddControllerToFilterList(controller, filters.ActionFilters);
    8.     ●AddControllerToFilterList(controller, filters.ResultFilters);
    9.     ●AddControllerToFilterList(controller, filters.AuthorizationFilters);
    10.     ●AddControllerToFilterList(controller, filters.ExceptionFilters);
    11.     return filters;
    12. }
    复制代码

    从上面代码的注释就可以理解这一点。AddControllerToFilterList方法就是把 Controller放入到Filter的行列中。下面为该方法的实现:

    1. private static void AddControllerToFilterList<TFilter>(ControllerBase controller, IList<TFilter> filterList) 
    2.             where TFilter : class {
    3.     TFilter controllerAsFilter = controller as TFilter;
    4.     if (controllerAsFilter != null) {
    5.         filterList.Insert(0, controllerAsFilter);
    6.     }
    7. }
    复制代码

    同样,它也先判断该Controller是否实现了某个类型Filter的接口,如果实现该类型Filter的接口就说明它是一个Filter,于是把它加入到相应类型的Filter列表中。到这里所有的Filter才真正全被收集起来。

    Filter的执行

    我们获取Filter是为了执行某些操作,所以下一步就是执行Filter的方法。由于执行各种类型的Filter 的过程比较复杂,涉及到的类也非常多,我打算把这节内容放到下一篇文章中介绍。同时在Filter执行也是一个承上启下的过程,我会在下一篇文章中详细的讲述。

    总结

    在本篇文章中,讲述的内容非常有限,主要讨论了怎么获取Filter的,以及Controller与各种 Filter的关系,还有Filter的4种类型,但是我们没有具体讨论4种类型Filter的方法。还有最终要的就是大家通过这个探究这个过程,真正了理解Filter的设计思想和AOP在mvc中的运用,以及Filter的功能强大之处。

  • 相关阅读:
    HDU 1982 Kaitou Kid The Phantom Thief (1)
    HDU 1984 Mispelling4
    HDU 2546 饭卡
    HDU 1009 FatMouse' Trade
    在VC 中如何隐藏一个主程序窗口
    .菜单项
    SetClassLong,GetClassLong 动态改变光标
    .窗口捕获鼠标
    .主窗口向子控件发送消息
    线段树 1698 Just a Hook 区间set更新
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2873483.html
Copyright © 2020-2023  润新知