在Asp.Net Core中使用ModelConvention实现全局过滤器隔

这来自于我把项目迁移到Asp.Net Core的过程中碰到一个问题。在一个web程序中同时包含了MVC和WebAPI,现在需要给WebAPI部分单独添加一个接口验证过滤器 IActionFilter ,常规做法一般是写好过滤器后给需要的控制器挂上这个标签,高级点的做法是注册一个全局过滤器,这样可以避免每次手动添加同时代码也更好管理。注册全局过滤器的方式为:

services.AddMvc(options => { options.Filters.Add(typeof(AccessControlFilter)); });

但这样做会带来一个问题,那就是MVC部分控制器也会受影响,虽然可以在过滤器中进行一些判断来区分哪些是MVC Controller哪些是API Controller,但是平白无故给MVC增加这么一个没用的Filter,反正我是不能忍,所以寻找有没有更好的办法来实现这个功能。

于是ModelConvention(可以翻译为模型约定)闪亮登场。

先认识下ApplicationModel

看一下官方文档是怎么描述应用程序模型(ApplicationModel)的:

ASP.NET Core MVC defines an application model representing the components of an MVC app. You can read and manipulate this model to modify how MVC elements behave. By default, MVC follows certain conventions to determine which classes are considered to be controllers, which methods on those classes are actions, and how parameters and routing behave. You can customize this behavior to suit your app's needs by creating your own conventions and applying them globally or as attributes.

简单一点说,ApplicationModel描述了MVC应用中的各种对象和行为,这些内容包含Application、Controller、Action、Parameter、Router、Page、Property、Filter等等,而Asp.Net Core框架本身内置一套规则(Convention)用来处理这些模型,同时也提供了接口给我们自定义约定来扩展模型以实现更符合需要的应用。

和应用程序模型有关的类都定义在命名空间 Microsoft.AspNetCore.Mvc.ApplicationModels 中,这些模型通过 IApplicationModelProvider 构建出来,Asp.Net Core框架提供的默认Provider是 DefaultApplicationModelProvider 。我们可以编辑这些模型,从而更改它的表现行为,这就要借助它的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 对象的实例,我们可以在它身上获取到的东西实在太多了,看看它包含些什么:

在Asp.Net Core中使用ModelConvention实现全局过滤器隔

有了这些,我们可以做很多很灵活的操作,例如通过设置 ControllerName 字段强制更改控制器的名称让程序中写死的控制器名失效,也可以通过 Filters 字段动态更新它的过滤器集合,通过 RouteValues 来更改路由规则等等。

说到这里,很多人会觉得这玩意儿和自定义过滤器看起来差不多,最开始我也这么认为,但经过实际代码调试我发现它的生命周期要比过滤器早的多,或者说根本无法比较, 这个家伙只需要在应用启动时执行一次并不用随着每次请求而执行 。也就是说,它的执行时间比激活控制器还要早,那时候根本没有过滤器什么事儿,它的调用是发生在 app.UseEndpoints() 。

回到最开始的需求。基于上面的介绍,我们可以自定义如下的约定:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wdsyfw.html