在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
从何说起 这来自于我把项目迁移到Asp.Net Core的过程中碰到一个问题。在一个web程序中同时包含了MVC和WebAPI,现在需要给WebAPI部分单独添加一个接口验证过滤器 services.AddMvc(options => { options.Filters.Add(typeof(AccessControlFilter)); }); 但这样做会带来一个问题,那就是MVC部分控制器也会受影响,虽然可以在过滤器中进行一些判断来区分哪些是MVC Controller哪些是API Controller,但是平白无故给MVC增加这么一个没用的Filter,反正我是不能忍,所以寻找有没有更好的办法来实现这个功能。 于是ModelConvention(可以翻译为模型约定)闪亮登场。 先认识下ApplicationModel 看一下官方文档是怎么描述应用程序模型(ApplicationModel)的:
简单一点说,ApplicationModel描述了MVC应用中的各种对象和行为,这些内容包含Application、Controller、Action、Parameter、Router、Page、Property、Filter等等,而Asp.Net Core框架本身内置一套规则(Convention)用来处理这些模型,同时也提供了接口给我们自定义约定来扩展模型以实现更符合需要的应用。 和应用程序模型有关的类都定义在命名空间 ModelConvention ModelConvention定义了操作模型的入口,又或者说是一种契约,通过它我们可以对模型进行修改,常用的Convention包括:
这些接口提供了一个共同的方法 namespace Microsoft.AspNetCore.Mvc.ApplicationModels { // // 摘要: // Allows customization of the Microsoft.AspNetCore.Mvc.ApplicationModels.ControllerModel. // // 言论: // To use this interface, create an System.Attribute class which implements the // interface and place it on a controller class. Microsoft.AspNetCore.Mvc.ApplicationModels.IControllerModelConvention // customizations run after Microsoft.AspNetCore.Mvc.ApplicationModels.IApplicationModelConvention // customizations and before Microsoft.AspNetCore.Mvc.ApplicationModels.IActionModelConvention // customizations. public interface IControllerModelConvention { // // 摘要: // Called to apply the convention to the Microsoft.AspNetCore.Mvc.ApplicationModels.ControllerModel. // // 参数: // controller: // The Microsoft.AspNetCore.Mvc.ApplicationModels.ControllerModel. void Apply(ControllerModel controller); } } 从接口摘要可以看到,这个接口允许自定义 有了这些,我们可以做很多很灵活的操作,例如通过设置 说到这里,很多人会觉得这玩意儿和自定义过滤器看起来差不多,最开始我也这么认为,但经过实际代码调试我发现它的生命周期要比过滤器早的多,或者说根本无法比较, 这个家伙只需要在应用启动时执行一次并不用随着每次请求而执行 。也就是说,它的执行时间比激活控制器还要早,那时候根本没有过滤器什么事儿,它的调用是发生在 回到最开始的需求。基于上面的介绍,我们可以自定义如下的约定: public class ApiControllerAuthorizeConvention : IControllerModelConvention { public void Apply(ControllerModel controller) { if (controller.Filters.Any(x => x is ApiControllerAttribute) && !controller.Filters.Any(x => x is AccessControlFilter)) { controller.Filters.Add(new AccessControlAttribute()); } } } 上面的主要思路就是通过判断控制器本身的过滤器集合是否包含 那么如何把这个约定注册到应用中呢?在Microsoft.AspNetCore.Mvc.MvcOptions中提供了 // // 摘要: // Gets a list of Microsoft.AspNetCore.Mvc.ApplicationModels.IApplicationModelConvention // instances that will be applied to the Microsoft.AspNetCore.Mvc.ApplicationModels.ApplicationModel // when discovering actions. public IList<IApplicationModelConvention> Conventions { get; } 通过操作它就能把自定义约定注入进去: services.AddMvc(options => { options.Conventions.Add(new ApiControllerAuthorizeConvention()); }) 细心的人会发现,Conventions是一个 通过代码调试发现,应用启动时遍历了系统中的所有控制器去执行Apply操作,那么通过 public class ApiControllerAuthorizeConvention : IApplicationModelConvention { public void Apply(ApplicationModel application) { foreach (var controller in application.Controllers) { if (controller.Filters.Any(x => x is ApiControllerAttribute) && !controller.Filters.Any(x => x is AccessControlFilter)) { controller.Filters.Add(new AccessControlFilter()); } } } } 再改进一下 实际开发中我的AccessControlFilter需要通过构造函数注入业务接口,类似于这样: public class AccessControlFilter : IActionFilter { private IUserService _userService; public AccessControlFilter(IUserService service) { _userService = service; } public void OnActionExecuting(ActionExecutingContext context) { //模拟一下业务操作 //var user=_userService.GetById(996); //....... } public void OnActionExecuted(ActionExecutedContext context) { } } 如何优雅的在Convention中使用DI自动注入呢?Asp.Net Core MVC框架提供的 controller.Filters.Add(new ServiceFilterAttribute(typeof(AccessControlFilter))); 当然了,要从DI中获取这个filter实例,必须要把它注入到DI容器中: services.AddScoped<AccessControlFilter>(); 至此,大功告成,继续愉快的CRUD。 突然想起来我上篇文章提到的扩展DI属性注入功能估计也能通过这个玩意实现,eeeeeee...有空了试一下。 总结 总体来说,我通过曲线救国的方式实现了全局过滤器隔离,虽然去遍历目标控制器再手动添加Filter的方式没有那种一行代码就能实现的方式优雅,但我大体来说还算满意,是目前能想到的最好办法。我估摸着, 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持极客世界。 |
请发表评论