【问题标题】:nested route using @ApiParam, how to use a custom validator in nestjs?使用@ApiParam 的嵌套路由,如何在nestjs 中使用自定义验证器?
【发布时间】:2021-01-02 15:53:47
【问题描述】:

我正在寻找有关 Nest 中自定义验证器和自定义装饰器的帮助。

第一种情况:工作一个

一个 DTO,带有类验证器注释:

import { IsNotEmpty, IsString } from 'class-validator';
import { IsOwnerExisting } from '../decorators/is-owner-existing.decorator';

export class CreatePollDto {
  @IsNotEmpty()
  @IsString()
  @IsOwnerExisting() // custom decorator, calling custom validator, using a service to check in db
  ownerEmail: string;

  @IsNotEmpty()
  @IsString()
  @NotContains(' ', { message: 'Slug should NOT contain any whitespace.' })
  slug: string;
}

我在控制器中使用它:

@Controller()
@ApiTags('/polls')
export class PollsController {
  constructor(private readonly pollsService: PollsService) {}

  @Post()
  public async create(@Body() createPollDto: CreatePollDto): Promise<Poll> {
    return await this.pollsService.create(createPollDto);
  }
}

当调用此端点时,dto 正在通过类验证器进行验证,并且我的自定义验证器可以工作。如果电子邮件不适合数据库中的任何用户,则会显示默认消息。 我是这么理解的。

第二种情况:如何让它发挥作用?

现在,我想做一些类似的事情,但在嵌套路由中,使用 ApiParam。我想使用自定义验证器检查参数是否与数据库中的某个对象匹配。
在那种情况下,我不能在 dto 中使用装饰器,因为 dto 不处理“slug”属性,它是 ManyToOne,而属性在另一边。

// ENTITIES
export class Choice {
  @ManyToOne((type) => Poll)
  poll: Poll;
}

export class Poll {
  @Column({ unique: true })
  slug: string;

  @OneToMany((type) => Choice, (choice) => choice.poll, { cascade: true, eager: true })
  @JoinColumn()
  choices?: Choice[];
}

// DTOs
export class CreateChoiceDto {
  @IsNotEmpty()
  @IsString()
  label: string;

  @IsOptional()
  @IsString()
  imageUrl?: string;
}

export class CreatePollDto {
  @IsNotEmpty()
  @IsString()
  @NotContains(' ', { message: 'Slug should NOT contain any whitespace.' })
  slug: string;

  @IsOptional()
  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => CreateChoiceDto)
  choices: CreateChoiceDto[] = [];
}

那么我应该在哪里挂钩我的验证?

我想直接在控制器中使用一些装饰器。也许这不是个好地方,我不知道。我也可以在服务中做到这一点。

@Controller()
@ApiTags('/polls/{slug}/choices')
export class ChoicesController {
  constructor(private readonly choicesService: ChoicesService) {}

  @Post()
  @ApiParam({ name: 'slug', type: String })
  async create(@Param('slug') slug: string, @Body() createChoiceDto: CreateChoiceDto): Promise<Choice> {
    return await this.choicesService.create(slug, createChoiceDto);
  }
}

在我的第一个案例中,我想使用类似以下的东西,但在控制器的 create 方法中。

@ValidatorConstraint({ async: true })
export class IsSlugMatchingAnyExistingPollConstraint implements ValidatorConstraintInterface {
  constructor(@Inject(forwardRef(() => PollsService)) private readonly pollsService: PollsService) {}

  public async validate(slug: string, args: ValidationArguments): Promise<boolean> {
    return (await this.pollsService.findBySlug(slug)) ? true : false;
  }

  public defaultMessage(args: ValidationArguments): string {
    return `No poll exists with this slug : $value. Use an existing slug, or register one.`;
  }
}

你明白我想做什么吗?可行吗?有什么好办法?

非常感谢!

【问题讨论】:

    标签: decorator nestjs class-validator


    【解决方案1】:

    如果您需要使用自定义规则验证 slug,您有两个选项之一

    1. 制作一个不使用类验证器并直接在其中进行验证的自定义管道。

    2. 使用@Param() { slug }: CreatePollDto。这假定所有内容都将通过 URL 参数发送。您总是可以使 DTO 变得简单,例如

    export class SlugDto {
      @IsNotEmpty()
      @IsString()
      @NotContains(' ', { message: 'Slug should NOT contain any whitespace.' })
      slug: string;
    }
    

    然后使用@Param() { slug }: SlugDto,现在Nest会通过ValidationPipe为你做验证。

    【讨论】:

      【解决方案2】:

      如果它对您的服务不起作用,请尝试使用

      getConnection().createQueryBuilder().select().from().where() 我在自定义装饰器中使用它来制作 isUnique 并且效果很好,但不支持可注入服务。

      public async validate(slug: string, args: ValidationArguments): Promise&lt;boolean&gt; { return (await getConnection().createQueryBuilder().select(PollsEntityAlias).from(PollsEntity).where('PollsEntity.slug =:slug',{slug}))) ? true : false; }

      【讨论】:

      • 我不认为我的问题是那个。并不是验证器没有工作,而是它没有在验证。我想知道我对如何在 dto 以外的地方使用验证器的理解:在控制器或服务中。
      • 我在上面的问题中添加了解释。但是,我发现 slug 验证器中存在错误的复制粘贴(它注入的是 UsersService 而不是 PollsService)。但这并没有改变我的疑问。
      【解决方案3】:

      太棒了!非常感谢,它正在工作。

      我尝试过类似的方法,但找不到好方法。

      解构的{ slug }: SlugDto,好狡猾又聪明!我试过slug : SlugDto,但它不起作用,我想«..hmmm...怎么做...»

      只是别的:在控制器方法中,我使用(如文档中所示)@Param('slug'),但使用 slugDto,它无法工作。相反,它必须只是@Param()

      最后,我的方法:

        @Post()
        @ApiParam({ name: 'slug', type: String })
        public async create(@Param() { slug }: SlugDto, @Body() createChoiceDto: CreateChoiceDto): Promise<Choice> {
          return await this.choicesService.create(slug, createChoiceDto);
        }
      

      还有 dto :

      export class SlugDto {
        @IsNotEmpty()
        @IsString()
        @NotContains(' ', { message: 'Slug should NOT contain any whitespace.' })
        @IsSlugMatchingAnyExistingPoll()
        slug: string;
      }
      

      【讨论】:

        猜你喜欢
        • 2021-04-22
        • 2019-05-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-10-30
        • 1970-01-01
        • 1970-01-01
        • 2020-01-04
        相关资源
        最近更新 更多