.NET MVC深度分析 扩展MVC内置过滤器
最近在公司项目中用MVC内置的权限过滤器实现权限控制功能,查阅以前的代码发现权限过滤接口已经被实现在其他地方,作用是用户访问系统任何页面时都要判断是否登录,如果没有登录,就会自动读取本机的域账号信息,并自动完成登录动作。现在遇到的问题是,如果新加的权限验证功能基于权限过滤器,就必须考虑过滤器的执行先后顺序,由于权限验证基于用户信息,所以我要保证用户自动登录的过滤器在权限过滤器之前被执行,但是MVC的权限过滤器默认是优先级最高的,我又不想让非权限验证的功能丢在权限过滤器中执行,又想要这个过滤器执行优先级一定高于权限过滤器,怎么办呢?看来只有通过扩展MVC内置过滤器的方式实现了。
一、初步计划
自定义一个过滤器接口IFirstFilter,它会在MVC框架中被最先调用,程序员只需要创建实现该接口的特性,就可以实现自定义过滤器。
二、实现步骤
1) 重写ControllerActionInvoker的方法InvokeAction
由于MVC框架的action方法调用统一通过InvokeAction执行,在这个方法中会调用MVC内置过滤器,我们通过重写它来给自定义过滤器“内置”进去。另外还自定义两个方法,GetFirstFilters方法用来通过反射特性获取自定义的特性,InvokeFirstFilters用来执行自定义的过滤器。
新建类文件UserControllerActionInvoker.cs,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | 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,代码如下:
1 2 3 4 5 6 7 | namespace MvcUserFilter.MVC { interface IFirstFilter { void OnFirstFilterDoing(FirstFilterContext filterContext); } } |
3) 定义自定义的过滤器上下文
新建类文件FirstFilterContext.cs,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | 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方法,代码如下:
1 2 3 4 5 6 7 8 9 10 | namespace MvcUserFilter.MVC { public class UserController : Controller { protected override IActionInvoker CreateActionInvoker() { return new UserControllerActionInvoker(); } } } |
三、使用方法
到此我们已经成功的将自定义的过滤器“内置”到MVC框架里了,现在看看怎么使用,使用方式跟MVC自带的过滤器基本一致(目前只能通过特性体现,不支持控制器虚方法派生):
首先,定义一个FirstFilterAttribute特性,从FilterAttribute, IFirstFilter 派生,实现接口IFirstFilter的方法OnFirstFilterDoing
1 2 3 4 5 6 7 8 9 10 11 | public class FirstFilterAttribute : FilterAttribute, IFirstFilter { public void OnFirstFilterDoing(FirstFilterContext filterContext) { filterContext.Result = new ContentResult() { Content= "用户自定义过滤器被执行了。" , }; } } |
然后,就可以在控制器的action上使用了,注意,控制器要从UserController继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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/)
版权声明:本文的版权归作者与博客园共有。转载时须注明本文指向型链接,否则作者将保留追究其法律责任。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库