如何实现VonaJS AOP编程中的全局中间件策略?

摘要:在VonaJS框架中,AOP编程包括三方面:控制器切面、内部切面和外部切面。控制器切面包括五能力:Middleware、Guard、Interceptor、Pipe、Filter。其中,Middleware又分为:局部中间件、全局中间件和系
在VonaJS框架中,AOP编程包括三方面:控制器切面、内部切面和外部切面。控制器切面包括五能力:Middleware、Guard、Interceptor、Pipe、Filter。 其中,Middleware又分为:局部中间件、全局中间件和系统中间件。其时序图如下所示: 由图可知,系统中间件在路由匹配之前执行,局部中间件和全局中间件在路由匹配之后执行。 为了简化起见,这里仅介绍全局中间件的用法,局部中间件和系统中间件的用法,请参见官方文档。 创建中间件 比如,在模块 demo-student 中创建一个 全局中间件: logger 1. Cli命令 $ vona :create:bean middleware logger --module=demo-student --boilerplate=cli/middlewareGlobal/boilerplate 2. 菜单命令 右键菜单 - [模块路径]: `Vona Aspect/Middleware Global` 中间件定义 export interface IMiddlewareOptionsLogger extends IDecoratorMiddlewareOptionsGlobal {} @Middleware<IMiddlewareOptionsLogger>({ global: true }) export class MiddlewareLogger extends BeanBase implements IMiddlewareExecute { async execute(_options: IMiddlewareOptionsLogger, next: Next) { const timeBegin = Date.now(); const res = await next(); const timeEnd = Date.now(); console.log('time: ', timeEnd - timeBegin); return res; } } IMiddlewareOptionsLogger: 定义中间件参数 execute: 输出执行时长 使用中间件 与局部中间件不同,系统会自动加载全局中间件,并使其生效 中间件参数 可以为中间件定义参数,通过参数更灵活的配置中间件逻辑 比如,为 logger 中间件定义prefix参数,用于控制输出格式 1. 定义参数类型 export interface IMiddlewareOptionsLogger extends IDecoratorMiddlewareOptionsGlobal { + prefix: string; } 2. 提供参数缺省值 @Middleware<IMiddlewareOptionsLogger>({ global: true, + prefix: 'time', }) 3. 使用参数 export interface IMiddlewareOptionsLogger extends IDecoratorMiddlewareOptionsGlobal { prefix: string; } @Middleware<IMiddlewareOptionsLogger>({ global: true, prefix: 'time', }) class MiddlewareLogger { async execute(options: IMiddlewareOptionsLogger, next: Next) { const timeBegin = Date.now(); const res = await next(); const timeEnd = Date.now(); - console.log('time: ', timeEnd - timeBegin); + console.log(`${options.prefix}: `, timeEnd - timeBegin); return res; } } 4. 使用时指定参数 可以针对某个 API 单独指定全局中间件的参数 + import { Aspect } from 'vona-module-a-aspect'; class ControllerStudent { @Web.get() + @Aspect.middlewareGlobal('demo-student:logger', { prefix: 'elapsed' }) async findMany() {} } 在使用中间件时直接提供参数值即可 5. App config配置 可以在 App config 中配置中间件参数 src/backend/config/config/config.ts // onions config.onions = { middleware: { 'demo-student:logger': { prefix: 'elapsed', }, }, }; 6. 参数优先级 使用时指定参数 > App config配置 > 参数缺省值 中间件顺序 由于全局中间件是默认加载并生效的,所以,VonaJS 提供了两个参数,用于控制中间件的加载顺序 1. dependencies 比如,系统有一个内置全局中间件a-core:gate,我们希望加载顺序如下:a-core:gate > Current @Middleware({ global: true, + dependencies: 'a-core:gate', prefix: 'time', }) class MiddlewareLogger {} 2. dependents dependents的顺序刚好与dependencies相反,我们希望加载顺序如下:Current > a-core:gate @Middleware({ global: true, + dependents: 'a-core:gate', prefix: 'time', }) class MiddlewareLogger {} 中间件启用/禁用 可以针对某些 API 控制全局中间件的启用/禁用 1. Enable 针对某个 API 禁用 class ControllerStudent { @Web.get() + @Aspect.middlewareGlobal('demo-student:logger', { enable: false }) async findMany() {} } 针对所有 API 禁用 src/backend/config/config/config.ts // onions config.onions = { middleware: { 'demo-student:logger': { + enable: false, }, }, }; 2. Meta 可以让全局中间件在指定的运行环境生效 名称 类型 说明 flavor string|string[] 参见: 运行环境与Flavor mode string|string[] 参见: 运行环境与Flavor instanceName string|string[] 参见: 多实例/多租户 host string|string[] 主机名 举例 @Middleware({ global: true, + meta: { + flavor: 'normal', + mode: 'dev', + instanceName: '', + host: 'localhost:7102', + }, }) class MiddlewareLogger {} 3. match/ignore 可以针对指定的 API 启用/禁用全局中间件 名称 类型 说明 match string|regexp|(string|regexp)[] 针对哪些API启用 ignore string|regexp|(string|regexp)[] 针对哪些API禁用 查看当前生效的全局中间件清单 可以直接在 Controller action 中输出当前生效的全局中间件清单 class ControllerStudent { @Web.get() async findMany() { + this.bean.onion.middleware.inspect(); } } this.bean.onion: 取得全局 Service 实例 onion .middleware: 取得与中间件相关的 Service 实例 .inspect: 输出当前生效的全局中间件清单 当访问findMany API 时,会自动在控制台输出当前生效的全局中间件清单,效果如下: 资源 Github:https://github.com/vonajs/vona 文档:https://vona.js.org/