6、Nest.js 中的管道与验证器

什么是管道(pipe)?

管道就是一个实现了 PipeTransform 接口并用 @Injectable() 装饰器修饰的类。
管道的作用简单来说就是,可以将输入的数据处理过后输出。

我们在前面的例子中将参数验证的逻辑写在了控制器中,这就打破了单一责任原则,控制器只应该处理请求的分发,验证的逻辑应该让验证器来做。
在 Nest 中正确的做法是,使用管道验证器,改写我们的 findOne 如下:

    @Get(':id')
    async findOne(@Param('id', new ParseIntPipe()) id): Promise {
        return await this.usersService.findOne(id);
    }

我们使用了 @nestjs/common 包中内置的一个管道 ParseIntPipe,它将一个字符串类型的数据转换成一个 int 类型,如果失败则抛出异常,现在访问 http://127.0.0.1:3000/users/exception :

{
    "statusCode":400,
    "date":"2018-7-31",
    "path":"/users/exception"
}

我们自定义的错误码又没了!这可不是我们想要的结果,看来还是需要自己定义管道验证器。
现在新建一个管道:

$ nest g pi users/pipes/user-id

CREATE /src/users/pipes/user-id/user-id.pipe.ts (216 bytes)

去掉 user-id/ 这一层目录,默认生成的管道使用的还是老版本的 @Pipe 装饰器, Nest 5.0 已经修改为 @Injectable 装饰器了,所以我们修改生成的代码再给它加上两行打印语句,如下:

import { ArgumentMetadata, PipeTransform, Injectable } from '@nestjs/common';

@Injectable()
export class UserIdPipe implements PipeTransform {
  async transform(value: any, metadata: ArgumentMetadata) {
    console.log(value);
    console.log(metadata);
    return value;
  }
}

PipeTransfrom 接口就只有一个 transfrom 方法, 其中的 value 参数就是我们需要处理的数据源,而 ArgumentMetadata 就是这个对象的一些 元数据(metadate),它的接口定义是下面这样的:

export interface ArgumentMetadata {
    type: 'body' | 'query' | 'param' | 'custom';
    metatype?: new (...args) => any;
    data?: string;
}

这里直接引用官方文档的说明:


6、Nest.js 中的管道与验证器_第1张图片
image.png

TypeScript接口在编译期间消失,所以如果你使用接口而不是类,那么元类型的值将是一个 Object。

现在让我们的管道工作起来,修改 findOne 如下:

    @Get(':id')
    async findOne(@Param('id', new UserIdPipe()) id): Promise {

        return await this.usersService.findOne(id);
    }

访问 http://127.0.0.1:3000/users/exception, 看到控制台输出:

exception
{ metatype: [Function: Object], type: 'param', data: 'id' }

我们已经在管道中拿到了参数的值和元数据,我们可以写自己的验证逻辑然后在验证失败的时候让他抛出 ApiException:

import { ArgumentMetadata, PipeTransform, Injectable, HttpStatus } from '@nestjs/common';
import { ApiException } from 'common/exceptions/api.exception';
import { ApiErrorCode } from 'common/enums/api-error-code.enum';

@Injectable()
export class UserIdPipe implements PipeTransform {
  async transform(value: any, metadata: ArgumentMetadata) {
   
    value = parseInt(value)

    if(isNaN(value) || typeof value !== 'number' || value <= 0) {
      throw new ApiException('用户ID无效', ApiErrorCode.USER_ID_INVALID, HttpStatus.BAD_REQUEST);
    }

    return value;
  }
}

访问 http://127.0.0.1:3000/users/exception,发现我们的管道工作的很好,我们自定义的业务状态码也回来了啦:

{
    "errorCode":10001,
    "errorMessage":"用户ID无效",
    "date":"2018-8-1",
    "path":"/users/exception"
}

本篇只介绍管道最基本的用法,在后面我们会循序渐进的学习管道的高级用法。

上一篇:5、Nest.js 中的异常处理和AOP编程
下一篇:7、Nest.js 中的类验证器

你可能感兴趣的:(6、Nest.js 中的管道与验证器)