.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] (http://www.cnblogs.com/lipan/)
版权声明:本文的版权归作者与博客园共有。转载时须注明本文指向型链接,否则作者将保留追究其法律责任。