使用模块功能提供者创建作用域模块

时间:2019-02-09 20:33:48

标签: javascript node.js typescript dependency-injection nestjs

当我多次使用导入PolicyModule.forFeature时,PolicyModule的下一次导入将覆盖PolicyStorage中的门。
当我尝试通过调用PolicyProvider

CandidateModule的{​​{1}}中使用CandidateEducationService
PolicyProvider

我收到异常await this.policy.denyAccessUnlessGranted('canDelete', education);

我在Gate by entity 'CandidateEducationEntity' not found中输出PolicyStorage并获得了CandidateEducationService的阵列门

JobPolicy

但是我期待着

PolicyStorage {
  gates:
    [ { policy: [Function: JobPolicy], entity: [Function: JobEntity] } ]
}

我创建了一个动态模块PolicyStorage { gates: [ { policy: [Function: CandidateEducationPolicy], entity: [Function: CandidateEducationEntity] } ] }

PolicyModule

@Module({}) export class PolicyModule { public static forFeature(gates: PolicyGate[]): DynamicModule { const providers: Provider[] = [ ...gates.map(gate => gate.policy), { provide: PolicyStorage, useValue: new PolicyStorage(gates), }, PolicyProvider, ]; return { module: PolicyModule, imports: [ CommonModule, ], providers, exports: providers, }; } }

PolicyStorage

@Injectable() export class PolicyStorage { constructor(private gates: PolicyGate[]) { console.log(this.gates); } public find(name: string): PolicyGate | null { return this.gates.find(policy => policy.entity.name === name); } }

PolicyProvider

在其他模块中使用模块。示例:
@Injectable() export class PolicyProvider<E, P> { constructor( private readonly moduleRef: ModuleRef, private readonly gateStorage: PolicyStorage, private readonly appContext: AppContextService, ) { } public async denyAccessUnlessGranted(methodNames: MethodKeys<P>, entity: E, customData?: any) { if (await this.denies(methodNames, entity, customData)) { throw new ForbiddenException(); } } public async allowAccessIfGranted(methodNames: MethodKeys<P>, entity: E, customData?: any) { const allowed = await this.allows(methodNames, entity, customData); if (!allowed) { throw new ForbiddenException(); } } private async allows(methodNames: MethodKeys<P>, entity: E, customData?: any): Promise<boolean> { const results = await this.getPolicyResults(methodNames, entity, customData); return results.every(res => res === true); } private async denies(methodNames: MethodKeys<P>, entity: E, customData?: any): Promise<boolean> { const results = await this.getPolicyResults(methodNames, entity, customData); return results.every(res => res === false); } private async getPolicyResults(methodNames: MethodKeys<P>, entity: E, customData?: any): Promise<boolean[]> { const methodNamesArray = Array.isArray(methodNames) ? methodNames : [methodNames]; const gate = this.findByClassName(entity.constructor.name); const user = this.appContext.get('user'); const policy = await this.moduleRef.get<P>(gate.policy, {strict: false}); const results = []; for (const methodName of methodNamesArray) { results.push(!!await policy[methodName as string](entity, user, customData)); } return results; } private findByClassName(name: string) { const gate = this.gateStorage.find(name); if (!gate) { throw new RuntimeException(`Gate by entity '${name}' not found`); } return gate; } }

JobsModule

@Module({ imports: [ TypeOrmModule.forFeature( [ JobEntity, ], ), PolicyModule.forFeature([ { policy: JobPolicy, entity: JobEntity, }, ]), ], controllers: [ ManagerJobsController, ], providers: [ ManagerJobsService, ], }) export class JobsModule { }

CandidateModule

1 个答案:

答案 0 :(得分:2)

更新:

Nest v6引入了请求范围的提供程序,请参见this answer


所有模块及其提供者均为单例。如果您在同一模块中两次使用同一令牌注册提供者,则它将被覆盖。

如果您查看TypeOrmModule,则可以在每个实体的唯一registers its repository providers下看到它custom token

export function getRepositoryToken(entity: Function) {
  if (
    entity.prototype instanceof Repository ||
    entity.prototype instanceof AbstractRepository
  ) {
    return getCustomRepositoryToken(entity);
  }
  return `${entity.name}Repository`;
}

因此,在您的情况下,您可以拥有功能getPolicyProviderTokengetPolicyStorageToken,并在这些标记下注册和注入您的提供程序,这些标记对于每个导入模块都是唯一的。