当我使用导入PolicyModule.forFeature
不止一次时,PolicyModule
的下一个导入会覆盖PolicyStorage
中的门。
当我尝试通过调用PolicyProvider
在CandidateModule
的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
@Module({
imports: [
TypeOrmModule.forFeature(
[
CandidateEducationEntity,
],
),
PolicyModule.forFeature([
{
policy: CandidateEducationPolicy,
entity: CandidateEducationEntity,
},
]),
],
controllers: [
CandidateEducationController,
],
providers: [
CandidateEducationService,
],
})
export class CandidateModule {
}
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`;
}
因此,在您的情况下,您可以使用getPolicyProviderToken
和getPolicyStorageToken
函数,并在这些令牌下注册和注入您的提供程序,这些令牌对于每个导入模块都是唯一的。