直接在控制器函数中验证枚举

JWo*_*JWo 0 rest typescript nestjs

我的 REST API 中有一个查询参数,其值应受枚举类型的限制。当客户端给出不同的东西时,我正在寻找一种抛出“错误请求”错误的方法。

我的枚举看起来像这样:

export enum Precision {
    S = 's',
    MS = 'ms',
    U = 'u',
    NS = 'ns',
}
Run Code Online (Sandbox Code Playgroud)

我的控制器功能如下所示:

  @Get(':deviceId/:datapoint/last')
  @ApiOkResponse()
  @ApiQuery({name: 'precision', enum: Precision})
  getLastMeasurement(
    @Param('deviceId') deviceId: string,
    @Param('datapoint') datapoint: string,
    @Query('precision') precision: Precision = Precision.S,
    @Res() response: Response,
  ) {
    console.log(precision);
    ....
    response.status(HttpStatus.OK).send(body);
  }
Run Code Online (Sandbox Code Playgroud)

我的问题是该函数也接受其他值(例如,我可以发送 f 作为查询参数的值)。该函数不会向客户端返回错误,但我不想在每个控制器函数的开头编写 if else 块。
我想对此有一个相当简单的解决方案,但是当我尝试在互联网上查找它时,我总是在 DTO 中获得类验证的结果,而不是直接在查询参数/REST 控制器中进行简单的枚举验证。

谢谢你的时间,
J

Rap*_*res 5

这里有2个问题。

第一个是您precision以错误的方式传递params的默认值。你必须DefaultValuePipe像这样使用:

getLastMeasurement(
  ... // some other params
    @Query('precision', new DefaultValuePipe(Precision.S)) precision: Precision
  ) { 
  ... // do something
}
Run Code Online (Sandbox Code Playgroud)

第二个是枚举验证。NestJS 只提供了 6 种验证管道,它们都没有验证枚举,因此您必须创建自己的自定义验证管道来验证枚举。

有两种可能的方法可以做到这一点:

  1. 创建自定义管道以仅验证您的特定枚举;
  2. 创建自定义管道泛型以验证任何枚举;

基于这个https://docs.nestjs.com/pipes#custom-pipes,它会是这样的:

  1. 仅验证特定的枚举
import { BadRequestException, PipeTransform } from '@nestjs/common';
import { isDefined, isEnum } from 'class-validator';

export class PrecisionValidationPipe implements PipeTransform<string, Promise<Precision>> {

  transform(value: string): Promise<Precision> {
    if (isDefined(value) && isEnum(value, Precision)) {
      return Precision[value];
    } else {
      const errorMessage = `the value ${value} is not valid. See the acceptable values: ${Object.keys(
        Precision
      ).map(key => Precision[key])}`;
      throw new BadRequestException(errorMessage);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

然后在您的请求中,就像

  getLastMeasurement(
    @Query('precision', PrecisionValidationPipe, new DefaultValuePipe(Precision.S)) precision: Precision,
  ) {
    console.log(precision);
    ....
    response.status(HttpStatus.OK).send(body);
  }
Run Code Online (Sandbox Code Playgroud)
  1. 验证任何枚举(我最喜欢的)
import { BadRequestException, Injectable, PipeTransform } from '@nestjs/common';
import { isDefined, isEnum } from 'class-validator';

@Injectable()
export class EnumValidationPipe implements PipeTransform<string, Promise<any>> {
  constructor(private enumEntity: any) {}
  transform(value: string): Promise<any> {
      if (isDefined(value) && isEnum(value, this.enumEntity)) {
        return this.enumEntity[value];
      } else {
        const errorMessage = `the value ${value} is not valid. See the acceptable values: ${Object.keys(this.enumEntity).map(key => this.enumEntity[key])}`;
        throw new BadRequestException(errorMessage);
      }
  }
}
Run Code Online (Sandbox Code Playgroud)

然后在您的请求中,就像

  getLastMeasurement(
    @Query('precision', new EnumValidationPipe(Precision), new DefaultValuePipe(Precision.S)) precision: Precision,
  ) {
    console.log(precision);
    ....
    response.status(HttpStatus.OK).send(body);
  }
Run Code Online (Sandbox Code Playgroud)


Jay*_*iel 4

您应该能够创建一个像LastMeasurementQueryParams这样使用类验证器装饰器的类,并使用内置的 ValidationPipe来检查并确保发送了预期值之一。

该类可能看起来像这样:

export class LastMeasurementQueryParams {

  @IsEnum(Precision)
  precision: Precision;
}
Run Code Online (Sandbox Code Playgroud)

然后你的控制器看起来像这样:

  @Get(':deviceId/:datapoint/last')
  @ApiOkResponse()
  @ApiQuery({name: 'precision', enum: Precision})
  getLastMeasurement(
    @Param('deviceId') deviceId: string,
    @Param('datapoint') datapoint: string,
    @Query('precision') precision: LastMeasurementQueryParams = { precision: Precision.S },
    @Res() response: Response,
  ) {
    console.log(precision);
    ....
    response.status(HttpStatus.OK).send(body);
  }
Run Code Online (Sandbox Code Playgroud)