Nest.js如何在自定义Class-Validator验证器中获取TypeORM仓库
解决Nest.js中class-validator自定义验证器注入Repository为null的问题
核心原因
class-validator的Constraint类默认不属于Nest.js的DI(依赖注入)容器管理范畴,直接使用@InjectRepository注入会失效,导致仓库实例为null。要复用已配置的TypeORM连接,必须让验证器纳入Nest的DI体系。
具体解决方案
1. 改造验证器类,加入Nest注入支持
给自定义验证器添加@Injectable()装饰器,让Nest负责实例化和依赖注入:
import { Injectable } from '@nestjs/common'; import { ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments } from 'class-validator'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { Parking } from './parking.entity'; @Injectable() // 关键:标记为Nest可注入的提供者 @ValidatorConstraint({ name: 'uniqueName', async: true }) export class UniqueNameConstraint implements ValidatorConstraintInterface { constructor( @InjectRepository(Parking) private readonly parkingRepo: Repository<Parking>, ) {} async validate(name: string, args: ValidationArguments) { // 现在可以正常执行数据库查询 const existingParking = await this.parkingRepo.findOne({ where: { name } }); return !existingParking; // 不存在则验证通过 } defaultMessage(args: ValidationArguments) { return `停车场名称「${args.value}」已存在`; } }
2. 在模块中注册验证器
将验证器加入对应模块的providers数组,同时确保已导入TypeORM的实体仓库:
import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Parking } from './parking.entity'; import { UniqueNameConstraint } from './unique-name.constraint'; @Module({ imports: [TypeOrmModule.forFeature([Parking])], // 注册Parking仓库 providers: [UniqueNameConstraint], // 注册验证器 exports: [UniqueNameConstraint], // 若其他模块需使用,可导出 }) export class ParkingModule {}
3. 配置ValidationPipe关联Nest容器
在main.ts中设置class-validator使用Nest的DI容器,确保验证器能获取到注入的实例:
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { ValidationPipe } from '@nestjs/common'; import { useContainer } from 'class-validator'; async function bootstrap() { const app = await NestFactory.create(AppModule); // 让class-validator复用Nest的DI容器 useContainer(app.select(AppModule), { fallbackOnErrors: true }); // 启用全局验证管道 app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true, })); await app.listen(3000); } bootstrap();
4. 自定义装饰器(可选)
封装验证器为可复用的装饰器,方便在DTO中使用:
import { registerDecorator, ValidationOptions } from 'class-validator'; import { UniqueNameConstraint } from './unique-name.constraint'; export function UniqueName(validationOptions?: ValidationOptions) { return (object: object, propertyName: string) => { registerDecorator({ target: object.constructor, propertyName, options: validationOptions, validator: UniqueNameConstraint, }); }; }
5. 在DTO中使用验证器
import { IsString, MaxLength } from 'class-validator'; import { UniqueName } from './unique-name.decorator'; export class CreateParkingDto { @IsString() @MaxLength(50) @UniqueName({ message: '该名称已被占用,请更换' }) name: string; }
备选方案(不推荐)
若不想依赖Nest的DI,可直接调用TypeORM的getRepository方法,但该方式耦合度高,不利于测试和维护:
import { getRepository } from 'typeorm'; import { Parking } from './parking.entity'; // 在validate方法中直接获取仓库 async validate(name: string) { const parkingRepo = getRepository(Parking); const existingParking = await parkingRepo.findOne({ where: { name } }); return !existingParking; }
内容的提问来源于stack exchange,提问作者H3lltronik
相关产品推荐
相关产品推荐

