0

0

Nest.js自定义验证器中TypeORM Repository的正确注入方法

霞舞

霞舞

发布时间:2025-10-31 14:43:00

|

1039人浏览过

|

来源于php中文网

原创

Nest.js自定义验证器中TypeORM Repository的正确注入方法

本文详细介绍了在nest.js应用中使用class-validator创建自定义异步验证器时,如何正确注入typeorm repository以进行数据库操作。通过将验证器类标记为@injectable并将其注册为模块提供者,同时引入typeormmodule.forfeature,确保repository实例能被nest.js依赖注入容器管理,从而解决repository为null的问题,实现数据库查询。

在Nest.js项目中,class-validator 库提供了一种强大且灵活的方式来定义数据传输对象(DTO)的验证规则。当需要进行异步验证,例如检查数据库中某个字段的唯一性时,我们通常会创建一个自定义的验证器。然而,一个常见的问题是在自定义验证器中尝试通过 @InjectRepository 注入 TypeORM Repository 时,发现 Repository 实例始终为 null。这通常是因为自定义验证器类没有被Nest.js的依赖注入(DI)容器正确管理。

问题分析

考虑以下自定义验证器代码,其目标是检查 name 字段在 Parking 实体中是否唯一:

import { InjectRepository } from '@nestjs/typeorm';
import {
  registerDecorator,
  ValidationOptions,
  ValidatorConstraint,
  ValidatorConstraintInterface,
  ValidationArguments,
} from 'class-validator';
import { Repository } from 'typeorm';
import { Parking } from '../entities/parking.entity';

@ValidatorConstraint({ async: true })
export class UniqueNameConstraint implements ValidatorConstraintInterface {
  constructor(
    @InjectRepository(Parking) private parkingRepository: Repository,
  ) {}

  async validate(name: any, args: ValidationArguments) {
    // 此时 parkingRepository 为 null
    const parking = await this.parkingRepository.findOne({ where: { name } });
    return !parking; // 如果找到,则表示不唯一,验证失败
  }
}

export function UniqueName(validationOptions?: ValidationOptions) {
  return function (object: object, propertyName: string) {
    registerDecorator({
      target: object.constructor,
      propertyName: propertyName,
      options: validationOptions,
      constraints: [],
      validator: UniqueNameConstraint,
    });
  };
}

并在 DTO 中使用:

import { Field, InputType } from '@nestjs/graphql'; // 假设使用 GraphQL
import { UniqueName } from './unique-name.validator'; // 引入自定义验证器

@InputType()
export class CreateEntityInput {
  @UniqueName({ message: 'The name already exists' })
  @Field(() => String, { description: 'Name of the entity' })
  name: string;
}

当运行此代码时,parkingRepository 在 UniqueNameConstraint 类的 validate 方法中会是 null。其根本原因在于,class-validator 在实例化 UniqueNameConstraint 时,并不知道它是一个需要Nest.js DI容器处理的类。因此,@InjectRepository 装饰器无法发挥作用,Repository 也就无法被注入。

解决方案

要解决此问题,我们需要确保 UniqueNameConstraint 类能够被Nest.js的依赖注入容器识别和管理。这需要进行以下两个关键步骤:

  1. 将验证器类标记为可注入: 使用 @Injectable() 装饰器标记 UniqueNameConstraint 类。
  2. 将验证器注册为模块提供者: 在相应的 Nest.js 模块中,将 UniqueNameConstraint 添加到 providers 数组中。
  3. 为实体导入 TypeORM 功能模块: 确保该模块通过 TypeOrmModule.forFeature() 导入了 Parking 实体。

步骤一:更新自定义验证器类

为 UniqueNameConstraint 添加 @Injectable() 装饰器。

Detect GPT
Detect GPT

一个Chrome插件,检测您浏览的页面是否包含人工智能生成的内容

下载
import { Injectable } from '@nestjs/common'; // 导入 Injectable
import { InjectRepository } from '@nestjs/typeorm';
import {
  registerDecorator,
  ValidationOptions,
  ValidatorConstraint,
  ValidatorConstraintInterface,
  ValidationArguments,
} from 'class-validator';
import { Repository } from 'typeorm';
import { Parking } from '../entities/parking.entity';

@Injectable() // 添加此行
@ValidatorConstraint({ async: true })
export class UniqueNameConstraint implements ValidatorConstraintInterface {
  constructor(
    @InjectRepository(Parking) private parkingRepository: Repository,
  ) {}

  async validate(name: any, args: ValidationArguments) {
    // 此时 parkingRepository 将被正确注入
    const parking = await this.parkingRepository.findOne({ where: { name } });
    return !parking;
  }
}

export function UniqueName(validationOptions?: ValidationOptions) {
  return function (object: object, propertyName: string) {
    registerDecorator({
      target: object.constructor,
      propertyName: propertyName,
      options: validationOptions,
      constraints: [],
      validator: UniqueNameConstraint,
    });
  };
}

步骤二:更新Nest.js模块

在 ParkingModule(或任何使用此验证器的模块)中,将 UniqueNameConstraint 添加到 providers 数组,并确保 TypeOrmModule.forFeature([Parking]) 已导入。

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Parking } from './entities/parking.entity'; // 假设 Parking 实体在这里
import { UniqueNameConstraint } from './unique-name.validator'; // 引入自定义验证器

@Module({
  imports: [
    TypeOrmModule.forFeature([Parking]) // 确保为 Parking 实体导入了 TypeORM 功能模块
  ],
  providers: [
    UniqueNameConstraint, // 将 UniqueNameConstraint 添加为提供者
    // 其他服务或提供者...
  ],
  // controllers: [...],
  // exports: [...],
})
export class ParkingModule {}

如果你的 Parking 实体是在 AppModule 中直接注册的,那么 AppModule 可能需要类似地配置:

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Parking } from './entities/parking.entity';
import { UniqueNameConstraint } from './unique-name.validator';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: 'database',
      port: 5432,
      username: 'postgres',
      password: 'postgres',
      database: 'postgres',
      entities: [Parking], // 直接引用实体类,而不是字符串路径
      synchronize: true,
    }),
    TypeOrmModule.forFeature([Parking]), // 即使在根模块也需要为实体注册forFeature
  ],
  providers: [UniqueNameConstraint],
})
export class AppModule {}

注意: 在 TypeOrmModule.forRoot 中使用 entities: [Parking] (直接引用类)而不是 entities: ['dist/**/*.entity{.ts,.js}'] (字符串路径)通常是更好的实践,可以提供更好的类型安全性和开发体验。

原理说明

通过上述修改,UniqueNameConstraint 现在被Nest.js的依赖注入容器所管理。当 class-validator 需要实例化 UniqueNameConstraint 时,Nest.js会介入并负责创建这个实例,并在创建过程中解析其构造函数中的依赖(即 @InjectRepository(Parking))。这样,parkingRepository 就能被正确地注入一个 TypeORM Repository 实例,从而允许你在自定义验证器中执行数据库查询。

总结与注意事项

  • 依赖注入是关键: 在Nest.js中,任何需要注入其他服务或Repository的类,都必须被Nest.js的DI容器管理。这意味着它通常需要 @Injectable() 装饰器,并作为 providers 注册到模块中。
  • TypeOrmModule.forFeature() 的作用: 这个方法告诉 TypeORM 哪些实体将在当前模块中使用,并为这些实体注册相应的 Repository。它是 @InjectRepository 装饰器能够找到并注入正确 Repository 的前提。
  • 模块划分: 建议将自定义验证器放置在与其相关的业务模块中,并在此模块中进行注册。例如,如果 UniqueNameConstraint 是用于 Parking 实体,那么将其注册到 ParkingModule 是一个良好的实践。
  • 异步验证: 确保你的 validate 方法是 async 的,并且在其中使用 await 等待数据库操作完成,因为数据库查询是异步的。

遵循这些指导原则,你就可以在Nest.js自定义验证器中成功注入 TypeORM Repository,实现强大的异步验证逻辑。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

232

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

437

2024.03.01

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

258

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

209

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1468

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

620

2023.11.24

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

550

2024.03.22

php中定义字符串的方式
php中定义字符串的方式

php中定义字符串的方式:单引号;双引号;heredoc语法等等。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

546

2024.04.29

Java JVM 原理与性能调优实战
Java JVM 原理与性能调优实战

本专题系统讲解 Java 虚拟机(JVM)的核心工作原理与性能调优方法,包括 JVM 内存结构、对象创建与回收流程、垃圾回收器(Serial、CMS、G1、ZGC)对比分析、常见内存泄漏与性能瓶颈排查,以及 JVM 参数调优与监控工具(jstat、jmap、jvisualvm)的实战使用。通过真实案例,帮助学习者掌握 Java 应用在生产环境中的性能分析与优化能力。

19

2026.01.20

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WEB前端教程【HTML5+CSS3+JS】
WEB前端教程【HTML5+CSS3+JS】

共101课时 | 8.4万人学习

JS进阶与BootStrap学习
JS进阶与BootStrap学习

共39课时 | 3.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号