Nest项目实战-接口参数校验

311 阅读5分钟

介绍

在nest中,校验参数是非常常用的功能,nest中也有许多内置的校验,通过管道来执行,但是在实际项目开发中,用的最多的还是通过ValidationPipe来实现自定义管道,那么下面就让我们来看下内置管道和自定义管道的实际应用吧。

内置管道验证

nest内置管道有这些:

  • ValidationPipe
  • ParseIntPipe
  • ParseBoolPipe
  • ParseArrayPipe
  • ParseUUIDPipe
  • DefaultValuePipe
  • ParseEnumPipe
  • ParseFloatPipe
  • ParseFilePipe

使用内置管道验证

现在我们对getHello接口增加一个name参数,如果我们不增加参数验证的话,无论是字符串还是数字或者浮点数都可以接收到,如下:

  @Get()
  getHello(@Query('name') name: string): string {
    return name;
  }

输入数字111能够正常返回:

输入字符串 野克 也能够正常返回:

但是如果我们给参数增加ParseIntPipe验证的话,就只能接受int类型参数,否则就返回报错信息,如下:

  getHello(@Query('name', ParseIntPipe) name: string): string {
    return name;
  }

输入数字123能够正常返回:

输入字符串 野克 就报错了:

是不是非常的简单!

接下来让我们来看下ParseIntPipe管道还能怎么玩

我们按住Ctrl或者command 点击 ParseIntPipe,进入ParseIntPipe的类型声明,如下图:

我们可以看到有个 options 参数,参数类型有三个:errorHttpStatusCode、exceptionFactory、optional

欧吼?意思是我们可以自定义错误状态码或者返回自定义函数,让我们来试下:

默认错误的返回code是400,现在我们让其返回502:

  @Get()
  getHello(
    @Query(
      'name',
      new ParseIntPipe({
        errorHttpStatusCode: HttpStatus.NOT_FOUND,
      }),
    )
    name: string,
  ): string {
    return name;
  }

返回结果如下:

我们试试自定义工厂函数:

  @Get()
  getHello(
    @Query(
      'name',
      new ParseIntPipe({
        exceptionFactory: (msg) => {
          throw new HttpException('666啊' + msg, HttpStatus.NOT_ACCEPTABLE);
        },
      }),
    )
    name: string,
  ): string {
    return name;
  }

返回结果如下:

注意:errorHttpStatusCode 的类型是有限制的,只能是 ErrorHttpStatusCode 类型,也就是说你不能返回200等非 ErrorHttpStatusCode 类型的值。

其他内置管道验证与上述案例大同小异,我就不一一演示了,你们可以自己试试。

使用自定义管道验证

模拟项目真实使用场景:假设我们需要写一个创建用户的post接口,需要接受两个参数:name、age,要求name必须是3-6位的字符串,age必须是数字类型。

OK,需求创建完成,通常我们要自定义验证会使用 class-validator这样一个工具包,里面有各种类型验证方法,方便我们,同时还需要下载 class-transformer这样一个转换工具包,放心,下面我们就会用到。

下载两个工具包:

pnpm i --save class-validator class-transformer

接下来让我们一起实现下,首先我们在实际做项目中会创建一个dto文件,这里通常就是定义接口参数的地方, 同时也会验证参数的类型,如下我们创建一个create-user.dto.ts文件添加对应的验证装饰器:

解释下 @Length() 装饰器:

我们将鼠标移入 @Length() 上会发现要求我们必须传入最小值,最大值选填,还可以配置验证信息,validationOptions我们传入message参数,自定义了校验提示语:

看一下 validationOptions 类型定义

接下来我们使用定义的 CreateUserDto:

  1. 导入CreateUserDto
  2. 改为post请求
  3. 使用Body装饰器接受参数,并且配置管道验证

让我们来试下吧:

可以看到,管道验证成功了,并且自动将错误信息返回了,并且name字段的验证信息是我们在 @Length() 装饰器中定义的信息,第二条验证信息的意思是age字段不是number类型,那我们按照提示修改下:

我们按照规则修改了传入的参数,但是还是提示age字段的类型不正确,这是为什么呢?

原因就浏览器通常会把接口的参数默认都转成字符串,所以我们虽然传的是number类型,但是经过浏览器自动转换,被动变成了string类型,我们可以先把验证装饰器去掉来看下打印信息:

那这可怎么办呢?

还记得我们安装的 class-transformer 这个转换工具包吗?就用它来实现:

在dto中增加 @Type() 装饰器,用来指定参数属性类型,这里我们定义的是number,那么增加这个装饰器后,会自动转换为number类型,然后再去验证。

现在虽然增加了 @Type() 装饰器,但是还是不能实现的,因为 ValidationPipe 管道默认是不允许转换的,需要手动开启:

顺便来看下 ValidationPipe 管道定义的类型

OK,这样我们就实现了参数的类型转换,让我们再试一下:

看下打印结果:

正常项目中我们使用 @Type() 装饰器就够用了,如果转换方式比较复杂并且有复杂转换逻辑的话,也可以使用 @Transform() 装饰器,接收一个函数来自定义转换逻辑:

例如:

import { Transform, Type } from "class-transformer";
import { IsNumber, IsString, Length,  } from "class-validator";


export class CreateUserDto {
    @IsString()
    @Length(3,6)
    name: string

    @IsNumber()
    @Transform((value) => parseInt(value.value))
    age:number
}

@Transform() 装饰器的作用与 @Type() 装饰器的作用是有区别的:

  • @Transform() 装饰器主要用于自定义转换逻辑,可以处理复杂的转换需求。
  • @Type() 装饰器主要用于指定属性的类型,特别是在处理嵌套对象时。

OK,这就是项目中接口参数验证的真实案例demo,道友学废了吗?

觉得写的还行的话烦请多多关注、支持,将持续更新中💪~