NestJS CanModifyGuard依赖注入失败问题求助
NestJS Guard依赖注入与循环依赖问题排查
问题描述
原本以为已掌握NestJS模块工作机制,在使用Guard时遇到如下错误:
Error: Nest can't resolve dependencies of the CanModifyGuard (UsersService, ?). Please make sure that the argument TelevisionsService at index [1] is available in the UsersModule context. Potential solutions: - If TelevisionsService is a provider, is it part of the current UsersModule? - If TelevisionsService is exported from a separate @Module, is that module imported within UsersModule? @Module({ imports: [ /* the Module containing TelevisionsService */ ] })
场景细节:
- 当
CanModifyGuard仅依赖TelevisionsService时,在TelevisionsController中使用正常 - 给Guard添加
UsersService依赖,并将其应用到UsersController后触发上述错误 - 尝试按错误提示将
TelevisionsModule导入UsersModule,触发循环依赖错误;使用forwardRef()未解决,疑似使用位置有误
相关代码文件
app.module.ts
import { MiddlewareConsumer, Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { MongooseModule } from '@nestjs/mongoose'; import { ConfigModule } from '@nestjs/config'; import { UsersModule } from './resources/users/users.module'; import { AuthModule } from './resources/auth/auth.module'; import { CommonModule } from './resources/common/common.module'; import { TelevisionsModule } from './resources/televisions/televisions.module'; import { CurrentUserMiddleware } from './common/middlewares/current-user.middleware'; @Module({ imports: [ ConfigModule.forRoot(), MongooseModule.forRoot(process.env.DATABASE_URL), UsersModule, AuthModule, CommonModule, TelevisionsModule, ], controllers: [AppController], providers: [AppService], }) export class AppModule { configure(consumer: MiddlewareConsumer) { consumer.apply(CurrentUserMiddleware).forRoutes('*'); } }
can-modify.guard.ts
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException, } from '@nestjs/common'; import { TelevisionsService } from '../../resources/televisions/televisions.service'; import { UsersService } from '../../resources/users/users.service'; @Injectable() export class CanModifyGuard implements CanActivate { constructor( private readonly usersService: UsersService, private readonly televisionsService: TelevisionsService, ) {} async canActivate(context: ExecutionContext): Promise<boolean> { //logics here } }
users.module.ts
import { Module } from '@nestjs/common'; import { UsersController } from './users.controller'; import { UsersService } from './users.service'; import { MongooseModule } from '@nestjs/mongoose'; import { User, UserSchema } from './user.entity'; @Module({ imports: [ MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]), ], exports: [UsersService], controllers: [UsersController], providers: [UsersService], }) export class UsersModule {}
televisions.module.ts
import { Module } from '@nestjs/common'; import { TelevisionsController } from './televisions.controller'; import { TelevisionsService } from './televisions.service'; import { MongooseModule } from '@nestjs/mongoose'; import { Television, TelevisionSchema } from './television.entity'; import { UsersModule } from '../users/users.module'; @Module({ imports: [ MongooseModule.forFeature([ { name: Television.name, schema: TelevisionSchema }, ]), UsersModule, ], exports: [TelevisionsService], controllers: [TelevisionsController], providers: [TelevisionsService], }) export class TelevisionsModule {}
auth.module.ts
import { Module } from '@nestjs/common'; import { AuthService } from './auth.service'; import { AuthController } from './auth.controller'; import { UsersModule } from '../users/users.module'; import { ConfigModule } from '@nestjs/config'; import authConfig from './config/auth.config'; @Module({ imports: [UsersModule, ConfigModule.forFeature(authConfig)], controllers: [AuthController], providers: [AuthService], }) export class AuthModule {}
common.module.ts
import { Module } from '@nestjs/common'; import { APP_GUARD } from '@nestjs/core'; import { AuthorizationRolesGuard } from '../../common/guards/authorization-roles.guard'; @Module({ providers: [ { provide: APP_GUARD, useClass: AuthorizationRolesGuard, }, ], }) export class CommonModule {}
问题根源
当在UsersController上使用CanModifyGuard时,Nest会尝试在UsersModule的上下文内解析该Guard的所有依赖。此时UsersModule未导入TelevisionsModule,导致无法找到TelevisionsService;但直接导入TelevisionsModule又会因为TelevisionsModule已经导入UsersModule而形成循环依赖。
解决方案
方案1:用forwardRef解决循环依赖+公共模块托管Guard
步骤1:给模块间的相互导入添加forwardRef
users.module.ts修改后:
import { Module, forwardRef } from '@nestjs/common'; import { UsersController } from './users.controller'; import { UsersService } from './users.service'; import { MongooseModule } from '@nestjs/mongoose'; import { User, UserSchema } from './user.entity'; import { TelevisionsModule } from '../televisions/televisions.module'; @Module({ imports: [ MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]), forwardRef(() => TelevisionsModule), ], exports: [UsersService], controllers: [UsersController], providers: [UsersService], }) export class UsersModule {}
televisions.module.ts修改后:
import { Module, forwardRef } from '@nestjs/common'; import { TelevisionsController } from './televisions.controller'; import { TelevisionsService } from './televisions.service'; import { MongooseModule } from '@nestjs/mongoose'; import { Television, TelevisionSchema } from './television.entity'; import { UsersModule } from '../users/users.module'; @Module({ imports: [ MongooseModule.forFeature([ { name: Television.name, schema: TelevisionSchema }, ]), forwardRef(() => UsersModule), ], exports: [TelevisionsService], controllers: [TelevisionsController], providers: [TelevisionsService], }) export class TelevisionsModule {}
步骤2:在CommonModule注册并导出CanModifyGuard
common.module.ts修改后:
import { Module } from '@nestjs/common'; import { APP_GUARD } from '@nestjs/core'; import { AuthorizationRolesGuard } from '../../common/guards/authorization-roles.guard'; import { CanModifyGuard } from '../../common/guards/can-modify.guard'; import { UsersModule } from '../users/users.module'; import { TelevisionsModule } from '../televisions/televisions.module'; @Module({ imports: [UsersModule, TelevisionsModule], providers: [ { provide: APP_GUARD, useClass: AuthorizationRolesGuard, }, CanModifyGuard, ], exports: [CanModifyGuard], }) export class CommonModule {}
步骤3:在需要的模块导入CommonModule
给UsersModule和TelevisionsModule的imports数组添加CommonModule,确保Guard能被注入。
方案2:直接在UsersModule注册Guard(无需公共模块)
如果不想用公共模块,可直接在UsersModule中注册CanModifyGuard,同时保留forwardRef处理循环依赖:
users.module.ts修改后:
import { Module, forwardRef } from '@nestjs/common'; import { UsersController } from './users.controller'; import { UsersService } from './users.service'; import { MongooseModule } from '@nestjs/mongoose'; import { User, UserSchema } from './user.entity'; import { TelevisionsModule } from '../televisions/televisions.module'; import { CanModifyGuard } from '../../common/guards/can-modify.guard'; @Module({ imports: [ MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]), forwardRef(() => TelevisionsModule), ], exports: [UsersService], controllers: [UsersController], providers: [UsersService, CanModifyGuard], }) export class UsersModule {}
内容的提问来源于stack exchange,提问作者Dora
相关产品推荐
相关产品推荐

