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

NestJS自定义ValidatorConstraint注入DataSource返回undefined

问题描述

实现自定义ValidatorConstraint校验数据表指定列是否存在目标值时,计划注入TypeORM的DataSource实例执行数据库查询,但验证器内访问this.dataSource返回undefined。

相关代码

DTO 代码

import { ApiProperty } from '@nestjs/swagger';
import { IsInt, IsNotEmpty, IsString, Max, Min } from 'class-validator';
import { Exists } from "../../common/decorators/exists.decorator";

export class CreateTeacherReviewDto {

    @ApiProperty()
    @IsNotEmpty()
    @IsInt()
    @Exists(['teacher:id'])
    readonly teacherId: number;
}

自定义验证器代码

import { BadRequestException, Injectable } from '@nestjs/common';
import {
    registerDecorator,
    ValidationArguments,
    ValidationOptions,
    ValidatorConstraint,
    ValidatorConstraintInterface,
} from 'class-validator';
import { DataSource } from "typeorm";

export function Exists(property: any, validationOptions?: ValidationOptions) {
    return (object: any, propertyName: string) => {
        registerDecorator({
            target: object.constructor,
            propertyName,
            options: validationOptions,
            constraints: [property],
            validator: ExistsConstraint,
        });
    };
}

@ValidatorConstraint({ name: 'Exists', async: true })
@Injectable()
export class ExistsConstraint implements ValidatorConstraintInterface {
    constructor(protected dataSource: DataSource) {}

    async validate(value: number | string, args: ValidationArguments) {
        const { constraints } = args;
        if (constraints.length === 0) {
            throw new BadRequestException(`Failed validating ${value} exists.`);
        }

        const str = constraints[0][0].split(':');
        const tableName = str[0];
        const columnName = str[1];

        console.log(this.dataSource); // 此处输出 undefined

        return false;

    }

    defaultMessage(args: ValidationArguments) {
        const { property, value } = args;

        return `${property} ${value} is already taken.`;
    }
}

package.json 依赖配置

"dependencies": {
        "@nestjs/common": "^8.0.0",
        "@nestjs/config": "^2.1.0",
        "@nestjs/core": "^8.0.0",
        "@nestjs/jwt": "^8.0.1",
        "@nestjs/mapped-types": "*",
        "@nestjs/passport": "^8.2.2",
        "@nestjs/platform-express": "^8.0.0",
        "@nestjs/swagger": "^5.2.1",
        "@nestjs/typeorm": "^8.1.4",
        "bcrypt": "^5.0.1",
        "class-transformer": "^0.5.1",
        "class-validator": "^0.13.2",
        "mysql2": "^2.3.3",
        "passport": "^0.6.0",
        "passport-jwt": "^4.0.0",
        "reflect-metadata": "^0.1.13",
        "rimraf": "^3.0.2",
        "rxjs": "^7.2.0",
        "swagger-ui-express": "^4.4.0",
        "typeorm": "^0.3.6"
    },

tsconfig.json 配置

{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "target": "es2017",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "incremental": true,
    "skipLibCheck": true,
    "strictNullChecks": false,
    "noImplicitAny": false,
    "strictBindCallApply": false,
    "forceConsistentCasingInFileNames": false,
    "noFallthroughCasesInSwitch": false
  }
}
问题原因

class-validator 默认自行实例化验证器类,不会走 NestJS 的依赖注入容器,即使给验证器类加了@Injectable()装饰器,构造函数声明的DataSource也不会被注入,访问时自然返回undefined。

解决方法

一共两步,缺一个都不行:

  1. 把ExistsConstraint验证器注册到对应Nest模块的providers数组里,让Nest的DI容器接管这个类的实例化,完成DataSource的注入。如果要全局使用,还要加到exports数组里。
  2. 应用启动时调用class-validator提供的useContainer方法,把验证器的实例化权限交给Nest DI容器,否则class-validator还是会自己new实例,注入逻辑不会生效。

具体操作

首先在存放验证器的模块(比如全局通用的CommonModule)中注册验证器:

import { Module } from '@nestjs/common';
import { ExistsConstraint } from './decorators/exists.decorator';

@Module({
  providers: [ExistsConstraint],
  exports: [ExistsConstraint]
})
export class CommonModule {}

然后在项目入口main.ts中添加useContainer配置:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { useContainer } from 'class-validator';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 核心配置,将class-validator的实例化权交给Nest容器
  useContainer(app.select(AppModule), { fallbackOnErrors: true });
  app.useGlobalPipes(new ValidationPipe())
  await app.listen(3000);
}
bootstrap();

配置完成后重启服务,this.dataSource就能正常获取到注入的TypeORM实例。

内容的提问来源于stack exchange,提问作者Rodrigo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 12:09:46