DTO验证入参
大约 6 分钟
DTO 验证入参
使用 DTO 可以清晰的了解对象的结构,使用 Pipes(管道)配合 class-validator 还可以对参数类型进行判断,还可以在验证失败的时候抛出错误信息。
什么是 DTO
数据传输对象(DTO)(Data Transfer Object),是一种设计模式之间传输数据的软件应用系统。
管道
管道和拦截器有点像,都是在数据传输过程中的“关卡”。
管道有两个类型:
- 转换:管道将输入数据转换为所需的数据输出;
- 验证:对输入数据进行验证,如果验证成功继续传递,验证失败则抛出异常;
创建管道
先创建 pipe 文件,在 common 目录下创建
nest g pipe validation common这里需要安装两个依赖包
pnpm install class-validator class-transformer -Svalidation.pipe.ts
import {
  ArgumentMetadata,
  Injectable,
  Logger,
  PipeTransform,
  ValidationError,
} from '@nestjs/common';
import { validate } from 'class-validator';
import { plainToClass } from 'class-transformer';
import { XAPIException } from '../api.exception';
import { EnumErrorCode } from '../../error/error.code';
@Injectable()
export class ValidationPipe implements PipeTransform {
  async transform(value: any, metadata: ArgumentMetadata) {
    const metatype = metadata.metatype;
    console.log(`value:`, value, 'metatype: ', metatype);
    // 如果没有传入验证规则,则不验证直接返回数据
    if (!metatype || !this.toValidate(metatype)) {
      return value;
    }
    const object = plainToClass(metatype, value);
    const errors = await validate(object);
    const errorList: string[] = [];
    const errObjList: ValidationError[] = [...errors];
    do {
      const e = errObjList.shift();
      if (!e) {
        break;
      }
      if (e.constraints) {
        for (const item in e.constraints) {
          errorList.push(e.constraints[item]);
        }
      }
      if (e.children) {
        errObjList.push(...e.children);
      }
    } while (true);
    if (errorList.length > 0) {
      const msg = errorList.join();
      Logger.error(`请求参数校验错误: ${msg}`);
      throw new XAPIException(
        EnumErrorCode.QUERY_PARAM_INVALID_FAIL,
        `请求参数校验错误: ${msg}`
      );
    }
    return object;
  }
  private toValidate(metatype: any): boolean {
    const types: any[] = [String, Boolean, Number, Array, Object];
    return !types.includes(metatype);
  }
}扩展Error
api.exception.ts
export class XAPIException extends Error {
  private m_ErrorCode: number;
  private m_URL?: string;
  private m_Method?: string;
  public constructor(
    paramErrorCode: number,
    paramErrMsg: string,
    paramURL?: string,
    paramMethod?: string
  ) {
    super(paramErrMsg);
    this.m_ErrorCode = paramErrorCode;
    this.name = 'XAPIException';
    this.m_URL = paramURL;
    this.m_Method = paramMethod;
  }
  public get errorCode() {
    return this.m_ErrorCode;
  }
  public get url() {
    return this.m_URL;
  }
  public get method() {
    return this.m_Method;
  }
  public toJSON() {
    return {
      errCode: this.m_ErrorCode,
      message: this.message,
      url: this.m_URL,
      method: this.method,
    };
  }
}自定义错误代码
error.code.ts
/** 项目的错误码 */
export enum EnumErrorCode {
  /** 系统错误 */
  FAIL = 500,
  /** 请求参数校验失败 */
  QUERY_PARAM_INVALID_FAIL = 10004,
}调整错误拦截
http.filter.ts
const message = exception.message;
let code = EnumErrorCode.FAIL;
let status = HttpStatus.OK;
if (exception instanceof XAPIException) {
  code = (exception as XAPIException).errorCode;
} else if (exception instanceof HttpException) {
  status = (exception as HttpException).getStatus();
} else {
  HttpStatus.INTERNAL_SERVER_ERROR;
}
response.status(status).json({
  code: code,
  msg: message,
  statusCode: status,
  timestamp: new Date().toISOString(),
  path: request.url,
});在 Dto 文件中增加错误提示
create-cat.dto.ts
import { IsNotEmpty, IsString } from 'class-validator';
export class CreateCatDto {
  @IsNotEmpty({ message: 'name不能为空' })
  @IsString({ message: 'name必须是String类型' })
  readonly name: string;
  @IsNotEmpty({ message: 'age不能为空' })
  readonly age: string;
  @IsNotEmpty({ message: 'breed不能为空' })
  readonly breed: string;
}绑定管道
用 @UsePipes() 装饰器来完成。
@UsePipes(new ValidationPipe())
@Post('createCat')
async create(@Body() createCatDto: CreateCatDto): Promise<ResultData> {
  this.catsService.create(createCatDto);
  return ResultData.ok();
}全局绑定
使用 useGlobalPipes 全局绑定管道
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { XHttpFilter } from './common/http.filter';
import { ValidationPipe } from './common/validation/validation.pipe';
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new XHttpFilter());
  app.useGlobalPipes(new ValidationPipe());
  await app.listen(3000);
}
bootstrap();验证效果
必填项传入空值
