怎么在Asp.Net中使用ModelConvention实现全局过滤器隔离?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
ModelConvention
ModelConvention定义了操作模型的入口,又或者说是一种契约,通过它我们可以对模型进行修改,常用的Convention包括:
IApplicationModelConvention
IControllerModelConvention
IActionModelConvention
IParameterModelConvention
IPageRouteModelConvention
这些接口提供了一个共同的方法 Apply
,方法参数是各自的应用程序模型,以 IControllerModelConvention
为例看一下它的定义:
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); } }
从接口摘要可以看到,这个接口允许自定义 ControllerModel
对象,而如何自定义内容正是通过 Apply
方法来实现,这个方法提供了当前 ControllerModel
对象的实例,我们可以在它身上获取到的东西实在太多了,看看它包含些什么:
有了这些,我们可以做很多很灵活的操作,例如通过设置 ControllerName
字段强制更改控制器的名称让程序中写死的控制器名失效,也可以通过 Filters
字段动态更新它的过滤器集合,通过 RouteValues
来更改路由规则等等。
说到这里,很多人会觉得这玩意儿和自定义过滤器看起来差不多,最开始我也这么认为,但经过实际代码调试我发现它的生命周期要比过滤器早的多,或者说根本无法比较, 这个家伙只需要在应用启动时执行一次并不用随着每次请求而执行 。也就是说,它的执行时间比激活控制器还要早,那时候根本没有过滤器什么事儿,它的调用是发生在 app.UseEndpoints()
。
回到最开始的需求。基于上面的介绍,我们可以自定义如下的约定:
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()); } } }
上面的主要思路就是通过判断控制器本身的过滤器集合是否包含 ApiControllerAttribute
来识别是否API Controller,如果是API Controller并且没有标记过 AccessControlAttribute
的话就新建一个实例加入进去。
那么如何把这个约定注册到应用中呢?在Microsoft.AspNetCore.Mvc.MvcOptions中提供了 Conventions
属性:
// // 摘要: // 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是一个 IApplicationModelConvention
类型的集合,而我们自定义的Convention是一个 IControllerModelConvention
,正常来说应该会报错才对?原因是Asp.Net Core的DI框架帮我们提供了一系列扩展方法来简化Convention的添加不用自己再去转换:
通过代码调试发现,应用启动时遍历了系统中的所有控制器去执行Apply操作,那么通过 IApplicationModelConvention
一样也能实现这个功能,因为它里面包含了控制器集合:
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框架提供的 ServiceFilter
可以解决这个问题, ServiceFilter
本身是一个过滤器,它的不同之处在于能够通过构造函数接收一个Type类型的参数,我们可以在这里把真正要用的过滤器传进去,于是上面的过滤器注册过程演变为:
controller.Filters.Add(new ServiceFilterAttribute(typeof(AccessControlFilter)));
当然了,要从DI中获取这个filter实例,必须要把它注入到DI容器中:
services.AddScoped<AccessControlFilter>();
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。