You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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-

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 20:57:21