.NET MVC深度分析 扩展MVC内置过滤器

  最近在公司项目中用MVC内置的权限过滤器实现权限控制功能,查阅以前的代码发现权限过滤接口已经被实现在其他地方,作用是用户访问系统任何页面时都要判断是否登录,如果没有登录,就会自动读取本机的域账号信息,并自动完成登录动作。现在遇到的问题是,如果新加的权限验证功能基于权限过滤器,就必须考虑过滤器的执行先后顺序,由于权限验证基于用户信息,所以我要保证用户自动登录的过滤器在权限过滤器之前被执行,但是MVC的权限过滤器默认是优先级最高的,我又不想让非权限验证的功能丢在权限过滤器中执行,又想要这个过滤器执行优先级一定高于权限过滤器,怎么办呢?看来只有通过扩展MVC内置过滤器的方式实现了。

一、初步计划

  自定义一个过滤器接口IFirstFilter,它会在MVC框架中被最先调用,程序员只需要创建实现该接口的特性,就可以实现自定义过滤器。

二、实现步骤

   1) 重写ControllerActionInvoker的方法InvokeAction

  由于MVC框架的action方法调用统一通过InvokeAction执行,在这个方法中会调用MVC内置过滤器,我们通过重写它来给自定义过滤器“内置”进去。另外还自定义两个方法,GetFirstFilters方法用来通过反射特性获取自定义的特性,InvokeFirstFilters用来执行自定义的过滤器。

  新建类文件UserControllerActionInvoker.cs,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Reflection;

namespace MvcUserFilter.MVC
{
    public class UserControllerActionInvoker : ControllerActionInvoker
    {
        public override bool InvokeAction(ControllerContext controllerContext, string actionName)
        {
            if (controllerContext != null && !string.IsNullOrEmpty(actionName))
            {
                ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);
                ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);

                if (actionDescriptor != null)
                {
                    IList<IFirstFilter> firstFilters = GetFirstFilters(actionDescriptor);
                    FirstFilterContext firstContext = InvokeFirstFilters(controllerContext, firstFilters, actionDescriptor);
                    if (firstContext.Result != null)
                    {
                        InvokeActionResult(controllerContext, firstContext.Result);
                        return true;
                    }
                }
            }

            return base.InvokeAction(controllerContext, actionName);

        }

        private IList<IFirstFilter> GetFirstFilters(ActionDescriptor actionDescriptor)
        {

            MethodInfo methodInfo = (actionDescriptor as ReflectedActionDescriptor).MethodInfo;

            FilterAttribute[] typeFilters = (FilterAttribute[])methodInfo.ReflectedType.GetCustomAttributes(typeof(FilterAttribute), true /* inherit */);
            FilterAttribute[] methodFilters = (FilterAttribute[])methodInfo.GetCustomAttributes(typeof(FilterAttribute), true /* inherit */);
            List<FilterAttribute> orderedFilters = typeFilters.Concat(methodFilters).OrderBy(attr => attr.Order).ToList();

            IList<IFirstFilter> firstFilters = new List<IFirstFilter>();

            foreach (FilterAttribute filter in orderedFilters)
            {
                IFirstFilter castFilter = filter as IFirstFilter;
                if (castFilter != null)
                {
                    firstFilters.Add(castFilter);
                }
            }
            return firstFilters;
        }

        private FirstFilterContext InvokeFirstFilters(ControllerContext controllerContext, IList<IFirstFilter> firstFilters, ActionDescriptor actionDescriptor)
        {
            FirstFilterContext context = new FirstFilterContext(controllerContext, actionDescriptor);
            foreach (IFirstFilter filter in firstFilters)
            {
                filter.OnFirstFilterDoing(context);
                if (context.Result != null)
                {
                    break;
                }
            }

            return context;
        }
    }
}

   2) 定义自定义的过滤器接口

  新建类文件IFirstFilter.cs,代码如下:

namespace MvcUserFilter.MVC
{
    interface IFirstFilter
    {
        void OnFirstFilterDoing(FirstFilterContext filterContext);
    }
}

   3) 定义自定义的过滤器上下文

  新建类文件FirstFilterContext.cs,代码如下:

namespace MvcUserFilter.MVC
{
    public class FirstFilterContext : ControllerContext
    {

        public FirstFilterContext()
        {
        }

        [Obsolete("The recommended alternative is the constructor AuthorizationContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor).")]
        public FirstFilterContext(ControllerContext controllerContext)
            : base(controllerContext)
        {
        }

        [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors",
            Justification = "The virtual property setters are only to support mocking frameworks, in which case this constructor shouldn't be called anyway.")]
        public FirstFilterContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
            : base(controllerContext)
        {
            if (actionDescriptor == null)
            {
                throw new ArgumentNullException("actionDescriptor");
            }

            ActionDescriptor = actionDescriptor;
        }

        public virtual ActionDescriptor ActionDescriptor
        {
            get;
            set;
        }

        public ActionResult Result
        {
            get;
            set;
        }

    }
}

   4) 重写控制器CreateActionInvoker方法,创建自定义ActionInvoker实例

  定义一个UserController.cs,继承自Controller,重写方法CreateActionInvoker方法,代码如下:

namespace MvcUserFilter.MVC
{
    public class UserController : Controller
    {
        protected override IActionInvoker CreateActionInvoker()
        {
            return new UserControllerActionInvoker();
        }
    }
}

三、使用方法

  到此我们已经成功的将自定义的过滤器“内置”到MVC框架里了,现在看看怎么使用,使用方式跟MVC自带的过滤器基本一致(目前只能通过特性体现,不支持控制器虚方法派生):

  首先,定义一个FirstFilterAttribute特性,从FilterAttribute, IFirstFilter 派生,实现接口IFirstFilter的方法OnFirstFilterDoing

    public class FirstFilterAttribute : FilterAttribute, IFirstFilter 
    {

        public void OnFirstFilterDoing(FirstFilterContext filterContext)
        {
            filterContext.Result = new ContentResult()
            {
                Content="用户自定义过滤器被执行了。",
            };
        }
    }

  然后,就可以在控制器的action上使用了,注意,控制器要从UserController继承

    public class HomeController : UserController
    {
        [FirstFilter]
        public ActionResult Index()
        {
            ViewData["Message"] = "欢迎使用 ASP.NET MVC!";

            return View();
        }

        public ActionResult About()
        {
            return View();
        }
    }

四、执行结果

  编译运行,访问首页时页面输出“用户自定义过滤器被执行了。”

作者:李盼(Lipan)
出处:[Lipan]http://www.cnblogs.com/lipan/
版权声明:本文的版权归作者与博客园共有。转载时须注明本文指向型链接,否则作者将保留追究其法律责任。
posted @ 2011-04-27 07:31  lipan  阅读(7207)  评论(18编辑  收藏  举报