typescript - 我可以直接訪問 DI 注入器來使用 NestJS 實例化類別嗎
我想知道是否有方法可以在 NestJS 中訪問 DI 注入器,並直接使用它來實例化類。這類似於 Angular 或 Inversify 直接暴露注入器以便在特殊情況下使用的方式。
多提供者可以解決我的使用情境。但在 NestJS 支援多提供者之前,我正在嘗試創建一個組合守衛,將多個守衛結合成一個,這樣我就可以全局應用需要存取 DI 的多個守衛。我希望組合守衛只接收一個類型陣列,然後使用注入器為這些類型生成對象。
我嘗試將 Injector 指定為工廠依賴,但 Nest 無法解析它。如果它可以解析,我也不知道該如何使用這個注入器。
@Module({
providers: [{
provide: APP_GUARD,
inject: [Injector],
useFactory: (injector: Injector) => {
return new MultiGuard([AuthGuard, PermissionGuard], injector);
}
}]
})
class AppModule {}
import { CanActivate, ExecutionContext, Type } from '@nestjs/common';
export class MultiGuard implements CanActivate {
constructor(
private readonly guards: Type<CanActivate>[],
private readonly injector: Injector
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const guards = this.guards.map(guard => {
return this.injector.get(guard);
});
const results = await Promise.all(guards);
return results.every(r => !!r);
}
}
答案#1
我最近做了類似的事情,但我不是將各種類型的守衛傳給我的「多重守衛」,而是直接在工廠中實例化守衛,然後傳入一個守衛實例的陣列。
這和 NestJS 執行守衛的方式非常相似。看看guard-consumer.ts檔案。
https://github.com/nestjs/nest/blob/master/packages/core/guards/guards-consumer.ts
@Injectable()
export class GuardsCollection implements CanActivate {
public constructor(private readonly guards: CanActivate[]) {}
public async canActivate(context: ExecutionContext): Promise<boolean> {
for (const guard of this.guards) {
const result = guard.canActivate(context);
if (!(await this.pickResult(result))) {
return false;
}
}
return true;
}
private async pickResult(result: boolean | Promise<boolean> | Observable<boolean>): Promise<boolean> {
if (result instanceof Observable) {
return result.toPromise();
}
return result;
}
}
在工廠中,我會實例化我的守衛。如果我的守衛有任何依賴項,我可以在那裡解決它們,方法是在 Inject 陣列中指定依賴項。可選的 inject 屬性接受一個提供者陣列,NestJS 會在實例化過程中解析這些提供者並將其作為參數傳遞給工廠函數。
@Module({
providers: [
{
provide: APP_GUARD,
useFactory: (reflector: Reflector) => {
return new GuardsCollection([
new (AuthGuard('jwt'))(),
new RolesGuard(reflector),
]);
},
inject: [Reflector],
},
],
})
export class AuthModule {}
另一個可能的選擇是注入一個 ModuleRef,並使用它在當前模組的上下文中解析項目。只要另一個模組被導入到當前模組中,你也應該能夠從另一個模組中獲取項目。
@Injectable()
export class MyGlobalGuard implements CanActivate {
public constructor(private readonly moduleRef: ModuleRef) {}
public async canActivate(context: ExecutionContext): Promise<boolean> {
// ... resolve items.
return true;
}
}
@Module({
providers: [
{
provide: APP_GUARD,
useClass: GlobalGuard,
},
],
})
export class AuthModule {}
