You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 06:34:54