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 -S
validation.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();
验证效果
必填项传入空值