如何在NestJS中间件中正确注入Service?解决依赖注入报错
我有一个用于验证用户配额(quota)的中间件,需要注入操作数据库的Service来检查配额。
中间件代码
export class PointMaxStorageMiddleware implements NestMiddleware { constructor(private readonly points: PointsService) {} async use(req: Request, res: Response, next: NextFunction) { const total = await this.points.count('user-id-test'); if (total >= LIMIT) throw new HttpException('error sample', 400); // ... } }
Service类代码
@Injectable() export class PointsService { private readonly logger = new Logger(PointsService.name); constructor( @InjectModel(Point.name) private readonly collection: Model<PointDocument>, ) {} async count(userId: string): Promise<number> { return await this.collection.countDocuments({ userId: userId }); } }
我的Service类已添加Injectable装饰器,且在项目其他模块中可正常使用,但将其注入中间件时,出现错误:
Cannot read properties of undefined (reading 'count')
注:count方法确实存在于该Service中。
我还尝试了以下写法:
constructor( @Inject(PointsService) private readonly points: PointsService, ) {}
此时出现错误:
Nest can't resolve dependencies of the PointsService (?). Please make sure that the argument PointModel at index [0] is available in the AppModule context.
Potential solutions:
- If PointModel is a provider, is it part of the current AppModule?
- If PointModel is exported from a separate @Module, is that module imported within AppModule? @Module({ imports: [ /* the Module containing PointModel */ ]})
Error: Nest can't resolve dependencies of the PointsService (?). Please make sure that the argument PointModel at index [0] is available in the AppModule context.
我也尝试在AppModule中添加自定义Provider:
providers: [ { provide: 'POINT_SERVICE', useClass: PointsService, }, ]
并修改构造函数:
constructor( @Inject('POINT_SERVICE') private readonly points: PointsService, ) {}
但仍出现上述相同的依赖解析错误。请问如何在NestJS中间件中正确注入Service?
解决步骤
1. 确保依赖模块正确导入与导出
PointsService依赖PointModel(来自Mongoose),所以必须保证中间件所在模块(如AppModule)已导入包含PointModel的业务模块(比如PointsModule),且该业务模块已导出PointsService:
// PointsModule.ts @Module({ imports: [MongooseModule.forFeature([{ name: Point.name, schema: PointSchema }])], providers: [PointsService], exports: [PointsService] // 导出Service,允许其他模块注入 }) export class PointsModule {}
在AppModule中导入该模块:
// AppModule.ts @Module({ imports: [PointsModule], // 其他配置 }) export class AppModule {}
2. 正确注册中间件
NestJS中间件有两种注册方式,需对应正确的注入逻辑:
方式一:模块内注册(推荐)
通过模块的configure方法注册中间件,Nest会自动处理依赖注入,中间件构造函数无需额外装饰器:
// AppModule.ts @Module({ imports: [PointsModule], }) export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(PointMaxStorageMiddleware) .forRoutes('your-target-route'); // 指定中间件生效的路由 } }
中间件构造函数保持原有写法即可:
constructor(private readonly points: PointsService) {}
方式二:全局注册
若需全局生效,需手动从应用容器中获取Service实例并传入中间件:
// main.ts async function bootstrap() { const app = await NestFactory.create(AppModule); const pointsService = app.get(PointsService); app.use(new PointMaxStorageMiddleware(pointsService)); await app.listen(3000); } bootstrap();
3. 避免重复声明Provider
不要在AppModule的providers中重复添加PointsService或自定义POINT_SERVICE,因为PointsService已在PointsModule中声明并导出,重复声明会导致Nest创建多个实例,引发依赖解析冲突。
内容的提问来源于stack exchange,提问作者Rafa Acioly

