typescript - Tjenesteinjeksjon i Guards og sirkulær avhengighet
Jeg sliter for øyeblikket med tjenesteinjeksjon i en vakt.
Vel, saken er at jeg lagde en JwtAuthGuard som trenger både min FirebaseService og min UserService.
Så jeg tenkte det ville være nyttig å lage en AuthGuardModule slik at jeg bare trenger å importere mine AuthGuardModules i modulene som har en Controller, for å bruke min Guard.
Saken er at dessverre trenger jeg denne vakten for min UserModule også. Så det er naturlig at jeg får en Circular Dependency Error når jeg importerer min AuthGuardModule i min UserModule.
Så prøvde jeg alle tingene mer eller mindre relatert til Circular Dependency Error i Nest.js, men det gikk ikke.
Kan noen fortelle meg om:
- Tar jeg helt feil ved å tenke på å lage en modul bare for vaktene mine?
- Hvis jeg ikke er det, hvordan kan jeg fikse min
Circular Dependency Error? - Hvis jeg misforstod den delen, hvorfor er det ikke riktig, og hva er den beste måten å gjøre det på?
JwtAuthGuard
@Injectable()
export class JwtAuthGuard implements CanActivate {
private readonly logger = new Logger(JwtAuthGuard.name)
constructor(
private readonly firebaseService: FirebaseService,
private readonly userService: UserService
) { }
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const token = request.headers['authorization'];
if (!token) {
throw new UnauthorizedException
}
try {
const firebaseUser = await this.firebaseService.getUserByToken(token)
request.user = await this.userService.getByEmail(firebaseUser.email)
return true
} catch (error) {
this.logger.error(this.canActivate.name, error)
throw new UnauthorizedException
}
}
}
AuthGuardModul
@Module({
imports: [UserModule],
providers: [FirebaseService, JwtAuthGuard],
exports: [JwtAuthGuard]
})
export class AuthGuardModule {}
REDIGER
Vel, jmcdo29 fra NestJS Discord forklarte meg at:
Din
AuthGuardModulebør eksportereUserModuleogFirebaseService. Guards, når de brukes i@UseGuards()-dekoratøren, brukes i konteksten av den gjeldende controllerens modul, i stedet for å bli sett på som enInjectable-leverandør
Så jeg gjorde det på den måten, og det løste mitt opprinnelige problem, men jeg har et annet problem med sirkulær avhengighet.
Først, la oss oppsummere arkitekturen jeg har:
AuthModule
- Den håndterer rutene for registrering, og rutene som sender noen e-poster som for eksempel tilbakestilling av passord eller e-postbekreftelse.
- Det trenger
UserModulefor å lage en bruker i databasen min når jeg kaller register-ruten
@Module({
imports: [
AuthGuardModule
],
controllers: [AuthController],
providers: [AuthService, EmailService, FirebaseService],
})
export class AuthModule {}
FileModule
- Den håndterer filopplastingen og slettingen.
@Module({
imports: [
forwardRef(() => AuthGuardModule),
BlobModule,
MongooseModule.forFeature([{ name: File.name, schema: FileSchema }])
],
controllers: [FileController],
providers: [FileService],
exports: [FileService],
})
export class FileModule {}
UserModule
- Den håndterer alle brukernes ruter.
- Den trenger
FileModule, for å sjekke om avataren eksisterer når den blir endret (jeg har bare en rute som oppdaterer alle mine brukeres personlige data) - Det trenger også
AuthGuardModulefor å bruke minJwtAuthGuard
@Module({
imports: [
forwardRef(() => AuthGuardModule),
forwardRef(() => FileModule),
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }])
],
controllers: [UserController],
providers: [UserService, FirebaseService],
exports: [UserService, FileModule]
})
export class UserModule {}
AuthGuardsModul
- Den inneholder min JWT-sjekkbeskyttelse, og deretter sjekker jeg om en bruker i databasen min er koblet til den tilkoblede brukeren
- Det trenger
UserModulefor å hente brukeren i databasen min
@Module({
imports: [forwardRef(() => UserModule), FirebaseModule],
providers: [JwtAuthGuard, RegisterAuthGuard],
exports: [JwtAuthGuard, RegisterAuthGuard, FirebaseModule, UserModule]
})
export class AuthGuardModule {}
Jeg kunne bruke Firebase for å lagre brukernes avatarer og gjøre det på frontend, men jeg må lagre det i Azure Blob Storage (det er pålagt for meg), så FileModule håndterer hele den saken
Så, nå som jeg løste min AuthGuardsModule -> UserModule sirkulære avhengighet takket være dere, har jeg et nytt problem med sirkulær avhengighet: AuthGuardsModule -> UserModule -> FileModule
Jeg satte en forwardRef av AuthGuardsModule inne i FileModule, men Nest sa naturlig til meg at jeg må sørge for at hver side av et toveis forhold er dekorert med "forwardRef()"
Så jeg la en forwardRef til min FileModule-import inne i min UserModule, men det samme skjer.
Noen idé?
Svar #1
Fra mitt perspektiv må du bruke forwardRef-metoden fra @nestjs/common-pakken, som har ansvaret for å håndtere sirkulære avhengigheter når de oppstår. Se Sirkulær avhengighet - NestJS-dokumentasjonen
En ting å ta i betraktning er plasseringen av din JwtAuthGuard. Var den opprinnelig inne i AuthModule eller i en annen delt modul?
Jeg vil anta at vakten opprinnelig var i din AuthModule (for eksempel la oss si /src/auth/guards/auth-guard.ts).
Jeg antar også at FirebaseService ligger innenfor en FirebaseModule og blir eksportert derfra, og at injiserbare fra FirebaseModule ikke trenger andre klasser som avhenger av andre sirkulært refererende klasser/moduler. (I så fall må du indikere for Nest hvordan det skal håndteres ved å legge til flere forwardRef-er i de involverte klassene/modulene)
Basert på koden du oppga, kunne du gjort noe slikt:
AuthModule
@Module({
imports: [forwardRef(() => UserModule), FirebaseModule],
providers: [...yourProviders],
exports: [...yourExportedInjectables]
})
export class AuthModule {}
FirebaseModule
@Module({
imports: [...requiredImports],
providers: [FirebaseService],
exports: [FirebaseService]
})
export class FirebaseModule {}
UserModule
@Module({
imports: [forwardRef(() => AuthModule)],
providers: [...yourProviders],
exports: [...yourExportedInjectables]
})
export class UserModule {}
NB: Du kan også bruke forwardRef på tjenestelaget avhengig av dine spesifikke brukstilfeller.
NB2: Guards kan grupperes innenfor en delt modul som kan importeres i AuthModule, slik at du ikke trenger å spesifisere Nest for å videresende referanser mellom hver modul, men bare referere til AuthModule i modulene som bruker den/de gitte guarden(e).
Håper det hjelper!
Igjen, jeg har ikke mye kontekst om arkitekturen din, så jeg har gitt et kodeeksempel basert på antakelser.
Ikke nøl med å kommentere/gi mer kontekst, så kan jeg redigere svaret mitt deretter.
