【问题标题】:Class-Validator (Node.js) Get another property value within custom validationClass-Validator (Node.js) 在自定义验证中获取另一个属性值
【发布时间】:2022-01-30 18:05:52
【问题描述】:

目前,我在 Nest.js 中有一个非常简单的类验证器文件,其中包含 ValidationPipe,如下所示:

import {
  IsDateString,
  IsEmail,
  IsOptional,
  IsString,
  Length,
  Max,
} from 'class-validator';

export class UpdateUserDto {
  @IsString()
  id: string;

  @Length(2, 50)
  @IsString()
  firstName: string;

  @IsOptional()
  @Length(2, 50)
  @IsString()
  middleName?: string;

  @Length(2, 50)
  @IsString()
  lastName: string;

  @IsEmail()
  @Max(255)
  email: string;

  @Length(8, 50)
  password: string;

  @IsDateString()
  dateOfBirth: string | Date;
}

假设在上面的“UpdateUserDto”中,用户传递了一个“email”字段。我想通过 class-validator 构建一个自定义验证规则,这样:

  • 检查电子邮件地址是否已被用户从数据库中获取
  • 如果电子邮件地址已被使用,检查当前用户(使用 'id' 属性的值)是否正在使用它,如果是,则验证通过,否则,如果它已被另一个用户使用,则验证失败。

虽然检查电子邮件地址是否已被使用是一项非常简单的任务,但您如何能够将 DTO 中其他属性的值传递给自定义装饰器 @IsEmailUsed

【问题讨论】:

    标签: node.js nestjs class-validator


    【解决方案1】:

    解决起来很简单,我通过创建一个自定义的类验证装饰器来解决它,如下所示:

    import { PrismaService } from '../../prisma/prisma.service';
    import {
      registerDecorator,
      ValidationOptions,
      ValidatorConstraint,
      ValidatorConstraintInterface,
      ValidationArguments,
    } from 'class-validator';
    import { Injectable } from '@nestjs/common';
    
    @ValidatorConstraint({ name: 'Unique', async: true })
    @Injectable()
    export class UniqueConstraint implements ValidatorConstraintInterface {
      constructor(private readonly prisma: PrismaService) {}
    
      async validate(value: any, args: ValidationArguments): Promise<boolean> {
        const [model, property = 'id', exceptField = null] = args.constraints;
    
        if (!value || !model) return false;
    
        const record = await this.prisma[model].findUnique({
          where: {
            [property]: value,
          },
        });
    
        if (record === null) return true;
    
        if (!exceptField) return false;
    
        const exceptFieldValue = (args.object as any)[exceptField];
        if (!exceptFieldValue) return false;
    
        return record[exceptField] === exceptFieldValue;
      }
    
      defaultMessage(args: ValidationArguments) {
        return `${args.property} entered is not valid`;
      }
    }
    
    export function Unique(
      model: string,
      uniqueField: string,
      exceptField: string = null,
      validationOptions?: ValidationOptions,
    ) {
      return function (object: any, propertyName: string) {
        registerDecorator({
          target: object.constructor,
          propertyName: propertyName,
          options: validationOptions,
          constraints: [model, uniqueField, exceptField],
          validator: UniqueConstraint,
        });
      };
    }
    

    但是,要允许对特定装饰器进行 DI,您还需要将其添加到您的 main.ts 引导函数中:

    async function bootstrap() {
      const app = await NestFactory.create(AppModule);
    
      ...
      // Line below needs to be added.
      useContainer(app.select(AppModule), { fallbackOnErrors: true });
      ...
    }
    

    另外,请确保在应用模块中导入“约束”:

    @Module({
      imports: ...,
      controllers: [AppController],
      providers: [
        AppService,
        PrismaService,
        ...,
        // Line below added
        UniqueConstraint,
      ],
    })
    export class AppModule {}
    

    最后,将其添加到您的 DTO 中:

    export class UpdateUserDto {
      @IsString()
      id: string;
    
      @IsEmail()
      @Unique('user', 'email', 'id') // Adding this will check in the user table for a user with email entered, if it is already taken, it will check if it is taken by the same current user, and if so, no issues with validation, otherwise, validation fails.
      email: string;
    }
    

    【讨论】:

      【解决方案2】:

      我认为您的第一个用例(检查电子邮件地址是否已被用户从数据库中获取)可以通过使用custom-validator来解决

      对于第二个,在验证之前没有获取当前用户的选项。假设您正在使用 @CurrentUser 装饰器获取当前用户。然后,一旦完成了正常的 dto 验证,您需要在控制器或服务内部检查当前用户是否正在访问您的资源。

      【讨论】:

        猜你喜欢
        • 2021-12-28
        • 2013-01-17
        • 2011-07-19
        • 2020-08-02
        • 1970-01-01
        • 1970-01-01
        • 2011-11-10
        • 1970-01-01
        • 2022-09-27
        相关资源
        最近更新 更多