如何在DynamicModule的registerAsync方法中访问ASYNC_OPTIONS参数值?
问题:如何让DynamicModule的registerAsync方法直接访问配置参数?
export class UppUserModule extends UserModuleClass { static register(options: typeof OPTIONS_TYPE): DynamicModule { const module = super.register(options); if (options.useFoo) { module.controllers.push(FooController); } return module; } static registerAsync(options: typeof ASYNC_OPTIONS_TYPE): DynamicModule { const module = super.registerAsync(options); //How to access options.useFoo return module; } }
现有如上所示的DynamicModule,在register方法中可直接访问options.useFoo参数值,并动态添加FooController;但在registerAsync方法中无法直接访问该参数值,仅能通过useFactory方法获取。请问如何让registerAsync方法具备与register方法相同的参数访问能力?
解决方案
1. 若useFoo为静态配置(无需异步解析)
如果useFoo只是一个同步开关,不需要从数据库、配置中心等异步源获取,直接把它从异步配置项中独立出来,作为registerAsync的顶层参数即可:
export class UppUserModule extends UserModuleClass { static register(options: typeof OPTIONS_TYPE): DynamicModule { const module = super.register(options); if (options.useFoo) { module.controllers.push(FooController); } return module; } // 扩展ASYNC_OPTIONS_TYPE,新增顶层的useFoo参数 static registerAsync(options: typeof ASYNC_OPTIONS_TYPE & { useFoo?: boolean }): DynamicModule { const module = super.registerAsync(options); // 现在可以直接访问options.useFoo if (options.useFoo) { module.controllers.push(FooController); } return module; } }
这种方案简单直接,适合useFoo和其他异步配置解耦的场景。
2. 若useFoo需异步解析(与其他配置联动)
如果useFoo是需要异步获取的,没法在registerAsync执行阶段直接拿到值,此时可以通过动态模块导入或者工厂函数拦截的方式实现:
方式A:封装Foo到独立模块,条件导入
把FooController封装成独立模块,根据异步解析出的useFoo值决定是否导入,这是最符合NestJS设计思路的方式:
// 先封装Foo模块 @Module({ controllers: [FooController], }) export class FooModule {} export class UppUserModule extends UserModuleClass { static register(options: typeof OPTIONS_TYPE): DynamicModule { const imports = options.useFoo ? [FooModule] : []; const baseModule = super.register(options); return { ...baseModule, imports: [...baseModule.imports, ...imports], }; } static registerAsync(options: typeof ASYNC_OPTIONS_TYPE): DynamicModule { // 创建临时模块,用来异步解析useFoo的值 const configResolverModule = Module({ providers: [ { provide: 'RESOLVED_USE_FOO', useFactory: async (...injectArgs) => { const fullConfig = await options.useFactory(...injectArgs); return fullConfig.useFoo; }, inject: options.inject || [], }, ], exports: ['RESOLVED_USE_FOO'], }); const baseModule = super.registerAsync(options); return { ...baseModule, imports: [ ...baseModule.imports, configResolverModule, // 动态条件导入FooModule { module: class DynamicFooWrapper {}, imports: [FooModule], condition: async (injector) => injector.get('RESOLVED_USE_FOO'), }, ], }; } }
方式B:拦截useFactory,拿到配置后处理控制器
通过包装原有的useFactory函数,在配置解析完成后动态添加控制器:
export class UppUserModule extends UserModuleClass { static register(options: typeof OPTIONS_TYPE): DynamicModule { const module = super.register(options); if (options.useFoo) { module.controllers.push(FooController); } return module; } static registerAsync(options: typeof ASYNC_OPTIONS_TYPE): DynamicModule { const originalFactory = options.useFactory; // 包装原工厂函数,拿到解析后的配置 const wrappedFactory = async (...injectArgs) => { const config = await originalFactory(...injectArgs); // 这里可以直接访问config.useFoo if (config.useFoo) { // 注意:如果模块已被父类初始化,直接修改controllers数组可能不生效 // 更稳妥的是通过provider触发控制器注册,或者结合模块初始化钩子 } return config; }; // 调用父类方法,传入包装后的工厂 const module = super.registerAsync({ ...options, useFactory: wrappedFactory, }); return module; } }
内容的提问来源于stack exchange,提问作者炸天帮二狗子
相关产品推荐
相关产品推荐

