Nest.js可配置中间件依赖注入问题:如何实现可复用注入型中间件?
Nest.js动态模块中注入依赖的可复用中间件解决方案
问题根源
问题出在模块上下文隔离:你在AppModule的configure方法中直接调用consumer.apply(AuthMiddleware)时,Nest会尝试在AppModule的依赖上下文里解析AuthMiddleware的依赖,但USER_SERVICE是在AuthModule内部提供的,AppModule上下文无法访问到这个提供者,因此报错。而模块初始化时的日志是AuthModule内部实例化中间件时输出的,和实际请求时的中间件实例并非同一个。
方案一:在AuthModule内部注册中间件(推荐)
把中间件的注册逻辑放到AuthModule内部,利用模块自身的上下文解析依赖,外部模块只需导入AuthModule即可。
修改AuthModule,让它实现NestModule接口:
@Module({ providers: [AuthService, AuthMiddleware], imports: [ExtModule], }) export class AuthModule extends createConfigurableDynamicRootModule< AuthModule, AuthModuleOptions >(AUTH_OPTIONS, { providers: [ { provide: AUTH_SECRET, inject: [AUTH_OPTIONS], useFactory: (options: AuthModuleOptions) => options.secret, }, { provide: USER_SERVICE, inject: [AUTH_OPTIONS], useFactory: (options: AuthModuleOptions) => options.userService, }, ], controllers: [AuthController], }) implements NestModule { configure(consumer: MiddlewareConsumer) { // 在AuthModule内部注册中间件,上下文正确 consumer.apply(AuthMiddleware).forRoutes('*'); } }
修改AppModule,移除自身的configure方法:
@Module({ imports: [ ConfigModule.forRoot(), AuthModule.forRootAsync(AuthModule, { imports: [ConfigModule, UserModule], inject: [ConfigService, UserService], useFactory: (config: ConfigService, userService: UserService) => { return { secret: config.get('AUTH_SECRET_VALUE'), userService, }; }, }) as DynamicModule, UserModule, ], controllers: [UserController], providers: [], }) export class AppModule {}
方案二:通过ModuleRef获取已实例化的中间件
如果必须在AppModule里注册中间件,可以使用ModuleRef获取AuthModule中已经初始化好的中间件实例(该实例已注入正确依赖)。
修改AppModule:
import { ModuleRef } from '@nestjs/core'; @Module({ imports: [ ConfigModule.forRoot(), AuthModule.forRootAsync(AuthModule, { imports: [ConfigModule, UserModule], inject: [ConfigService, UserService], useFactory: (config: ConfigService, userService: UserService) => { return { secret: config.get('AUTH_SECRET_VALUE'), userService, }; }, }) as DynamicModule, UserModule, ], controllers: [UserController], providers: [], }) export class AppModule implements NestModule { constructor(private readonly moduleRef: ModuleRef) {} configure(consumer: MiddlewareConsumer) { // 从ModuleRef获取AuthModule中已实例化的中间件 const authMiddleware = this.moduleRef.get(AuthMiddleware); consumer.apply(authMiddleware).forRoutes('*'); } }
方案三:导出中间件并暴露依赖(不推荐)
如果一定要让AppModule能解析AuthMiddleware的依赖,可以把USER_SERVICE和AuthMiddleware从AuthModule导出,但这种方式会破坏模块封装性,不建议使用:
修改AuthModule的动态模块配置,添加exports:
export class AuthModule extends createConfigurableDynamicRootModule< AuthModule, AuthModuleOptions >(AUTH_OPTIONS, { providers: [ { provide: AUTH_SECRET, inject: [AUTH_OPTIONS], useFactory: (options: AuthModuleOptions) => options.secret, }, { provide: USER_SERVICE, inject: [AUTH_OPTIONS], useFactory: (options: AuthModuleOptions) => options.userService, }, ], controllers: [AuthController], exports: [USER_SERVICE, AuthMiddleware], // 导出依赖和中间件 }) {}
内容的提问来源于stack exchange,提问作者JH-
相关产品推荐
相关产品推荐

