NestJS自定义验证器无法注入Service:buildingZoneService为undefined
NestJS自定义验证器依赖注入失败问题
问题描述
我正尝试为NestJS中的输入类创建自定义验证器,已完成以下实现,但每次调用注入的buildingZoneService时,它始终为undefined。我已经给Constraint类添加了@Injectable装饰器,在模块中导入了所有外部依赖,并且在main.ts中调用了class-validator的useContainer函数,完全按照相关配置说明操作,但问题依然存在。
代码实现
1. 自定义验证器约束类 (is-building-id-provided.validator.ts)
// is-building-id-provided.validator.ts import { Injectable } from "@nestjs/common"; import { AddToQueueInput } from "@warp-core/building-queue/input/add-to-queue.input"; import { BuildingZoneService } from "@warp-core/building-zone/building-zone.service"; import { registerDecorator, ValidationArguments, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface } from "class-validator"; @ValidatorConstraint({ async: true }) @Injectable() export class IsBuildingIdProvidedConstraint implements ValidatorConstraintInterface { constructor( protected readonly buildingZoneService: BuildingZoneService ) {} async validate(buildingId: number, args: ValidationArguments) { if (buildingId) { return true; } const addToQueue = args.object as AddToQueueInput; const buildingZone = await this.buildingZoneService.getSingleBuildingZone( addToQueue.counterPerHabitat ); if (!buildingZone.buildingId) { return false; } return true; } defaultMessage(args: ValidationArguments) { return `Building Id is required to create new building on existing building zone`; } } export function IsBuildingIdProvided(validationOptions?: ValidationOptions) { return function (object: Object, propertyName: string) { registerDecorator({ target: object.constructor, propertyName: propertyName, options: validationOptions, constraints: [], validator: IsBuildingIdProvidedConstraint, }); }; }
2. 输入类 (add-to-queue.input.ts)
// add-to-queue.input.ts import { Field, InputType, Int, PartialType } from "@nestjs/graphql"; import { IsBuildingIdProvided } from "@warp-core/building-queue/input/validator/is-building-id-provided.validator"; import { BuildingQueueElementModel } from "@warp-core/database/model/building-queue-element.model"; @InputType({description: "Creates new element in queue"}) export class AddToQueueInput extends PartialType(BuildingQueueElementModel) { @Field(type => Int, { description: "Id of building type that will be constructed. If building is already placed, that field will be ignored" }) @IsBuildingIdProvided() buildingId?: number; @Field(type => Int, {description: "How much levels will be build"}) endLevel: number; }
3. 模块配置 (building-queue.module.ts)
// building-queue.module.ts import { Module } from "@nestjs/common"; import { ConfigModule } from "@nestjs/config"; import { AuthModule } from "@warp-core/auth/auth.module"; import { BuildingQueueAddService } from "@warp-core/building-queue/building-queue-add.service"; import { BuildingQueueResolver } from "@warp-core/building-queue/building-queue.resolver"; import { IsBuildingIdProvidedConstraint } from "@warp-core/building-queue/input/validator/is-building-id-provided.validator"; import { BuildingZoneModule } from "@warp-core/building-zone/building-zone.module"; import { BuildingModule } from "@warp-core/building/building.module"; import { DatabaseModule } from "@warp-core/database/database.module"; @Module({ providers: [ BuildingQueueAddService, BuildingQueueResolver, IsBuildingIdProvidedConstraint, ], imports: [ BuildingZoneModule, BuildingModule, DatabaseModule, ConfigModule, AuthModule, ], exports: [ ] }) export class BuildingQueueModule { }
4. 应用入口 (main.ts)
// main.ts import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { AppModule } from '@warp-core/app.module'; import { useContainer } from 'class-validator'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes( new ValidationPipe({ whitelist: true, transform: true, }), ); useContainer(app.select(AppModule), { fallbackOnErrors: true }); await app.listen(3000); console.log("App is loaded at " + (await app.getUrl()) + "/graphql URL"); } bootstrap();
解决方案
1. 调整useContainer调用顺序
依赖注入失败的核心原因是**useContainer调用晚于ValidationPipe初始化**,class-validator需要先获取容器实例才能正确解析依赖。修改main.ts,将useContainer移到app.useGlobalPipes之前:
// main.ts 修改后 import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { AppModule } from '@warp-core/app.module'; import { useContainer } from 'class-validator'; async function bootstrap() { const app = await NestFactory.create(AppModule); // 先绑定容器,再初始化验证管道 useContainer(app.select(AppModule), { fallbackOnErrors: true }); app.useGlobalPipes( new ValidationPipe({ whitelist: true, transform: true, }), ); await app.listen(3000); console.log("App is loaded at " + (await app.getUrl()) + "/graphql URL"); } bootstrap();
2. 确保服务被正确导出
检查BuildingZoneModule是否导出了BuildingZoneService,否则即使导入模块,其他模块也无法注入该服务:
// building-zone.module.ts 修改后 @Module({ // 其他配置... exports: [BuildingZoneService], }) export class BuildingZoneModule {}
完成以上两项修改后,自定义验证器中的buildingZoneService即可正常注入。
内容的提问来源于stack exchange,提问作者ptyskju
相关产品推荐
相关产品推荐

