使用模块功能的提供程序创建范围模块

问题描述 投票:2回答:1

当我使用导入PolicyModule.forFeature不止一次时,PolicyModule的下一个导入会覆盖PolicyStorage中的门。 当我尝试通过调用PolicyProviderCandidateModuleCandidateEducationService中使用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

@Module({
    imports: [
        TypeOrmModule.forFeature(
            [
                CandidateEducationEntity,
            ],
        ),
        PolicyModule.forFeature([
            {
                policy: CandidateEducationPolicy,
                entity: CandidateEducationEntity,
            },
        ]),
    ],
    controllers: [
        CandidateEducationController,
    ],
    providers: [
        CandidateEducationService,
    ],
})
export class CandidateModule {
}
javascript node.js typescript dependency-injection nestjs
1个回答
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函数,并在这些令牌下注册和注入您的提供程序,这些令牌对于每个导入模块都是唯一的。

© www.soinside.com 2019 - 2024. All rights reserved.