DTO
DTO 的作用就是验证数据类型
DTO 离不开管道,通过管道抛出异常,被全局过滤器捕获
前期准备
安装依赖
bash
pnpm i class-validator class-transformer -S
新建全局管道
- 抛出异常,需要使用全局过滤器捕获
ts
import {
PipeTransform,
Injectable,
ArgumentMetadata,
HttpException,
} from "@nestjs/common";
import { plainToInstance } from "class-transformer";
import { validate } from "class-validator";
@Injectable()
export class ValidationPipe implements PipeTransform {
async transform(value: any, metadata: ArgumentMetadata) {
const { metatype } = metadata;
console.log(metatype);
if (!metatype || !this.toValidate(metatype)) {
return value;
}
// 变成对象,把规则和值 组合成验证的对象
const object = plainToInstance(metatype, value);
// 验证 errors 是个数组
const errors = await validate(object);
if (errors.length > 0) {
// 第一种只返回第一个
if (errors.length == 1) {
for (let arr in errors[0].constraints) {
throw new HttpException(`${errors[0].constraints[arr]}`, 302);
}
}
// 第二种返回所有
let result = [];
errors.forEach((item) => {
for (let arr in item.constraints) {
result.push({
message: item.constraints[arr],
field: item.property,
});
}
});
throw new HttpException(result, 302);
}
return object;
}
private toValidate(metatype: Function): boolean {
// 白名单
const types: Function[] = [String, Boolean, Number, Array, Object];
return !types.includes(metatype);
}
}
- 不抛出异常
DTO 里面就得改写下
ts
import { IsNotEmpty, IsInt } from "class-validator";
import { CustomNotEmpty } from "./customdto";
export class LoginDto {
@CustomNotEmpty()
@IsNotEmpty({ message: "用户名不能为空" })
username: String;
@IsInt({ message: "密码只能是数字" })
password: number;
// 加一个属性用来判断是否验证通过
status?: String;
}
管道里面改写
ts
import {
PipeTransform,
Injectable,
ArgumentMetadata,
HttpException,
} from "@nestjs/common";
import { plainToInstance } from "class-transformer";
import { validate } from "class-validator";
@Injectable()
export class ValidationPipe implements PipeTransform {
async transform(value: any, metadata: ArgumentMetadata) {
const { metatype } = metadata;
console.log(metatype);
if (!metatype || !this.toValidate(metatype)) {
return value;
}
// 变成对象,把规则和值 组合成验证的对象
const object = plainToInstance(metatype, value);
// 验证 errors 是个数组
const errors = await validate(object);
if (errors.length > 0) {
// 第一种只返回第一个
if (errors.length == 1) {
for (let arr in errors[0].constraints) {
// throw new HttpException(`${errors[0].constraints[arr]}`, 302);
return {
code: 200,
status: "error",
message: `${errors[0].constraints[arr]}`,
};
}
}
// 第二种返回所有
let result = [];
errors.forEach((item) => {
for (let arr in item.constraints) {
result.push({
message: item.constraints[arr],
field: item.property,
});
}
});
return {
code: 200,
status: "error",
message: result,
};
}
return object;
}
private toValidate(metatype: Function): boolean {
// 白名单
const types: Function[] = [String, Boolean, Number, Array, Object];
return !types.includes(metatype);
}
}
新建全局过滤器
ts
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
HttpStatus,
} from "@nestjs/common";
import { Request, Response } from "express";
@Catch(HttpException)
export class GlobalExceptionFilter implements ExceptionFilter {
catch(exception: any, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
// 状态码
const status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
// 异常消息 要是多个的话 message只会返回httpexception,所以最好单个
const message =
exception instanceof HttpException ? exception.message : exception.stack;
response.status(status).json({
code: status,
timestamp: new Date().toLocaleString(),
path: request.url,
message: message,
response: exception.response,
});
response.status(status).json();
}
}
绑定到全局
ts
import { Module } from "@nestjs/common";
import { LoginModule } from "./Login/login.module";
// 全局管道
import { ValidationPipe } from "./common/Pipe/global.pipe";
// 全局异常
import { GlobalExceptionFilter } from "./common/exception/global.filter";
// 类型
import { APP_FILTER, APP_PIPE } from "@nestjs/core";
@Module({
imports: [LoginModule],
controllers: [],
providers: [
{
provide: APP_PIPE,
useClass: ValidationPipe,
},
{
provide: APP_FILTER,
useClass: GlobalExceptionFilter,
},
],
})
export class AppModule {}
使用 DTO
新建 DTO 文件
起名叫 xxx.dto
- 要验证的参数
ts
import { IsInt, IsNotEmpty, IsString, Max, Min } from "class-validator";
export class LoginDto {
@Min(6, { message: "用户名不能小于6位" })
@IsNotEmpty({ message: "用户名不能为空" })
username: string;
@IsNotEmpty({ message: "密码不能为空" })
@Max(20, { message: "密码长度不能超过20位" })
@Min(6, { message: "密码长度不能小于6位" })
password: number;
}
控制器内使用
ts
//1.引入
import { LoginDto } from '../../common/dto/Login';
//2 使用loginDto
@Get('test')
getLogins(@Query() Login: LoginDto): Promise<string> {
console.log('进入控制器了');
if (loginDto.status == 'error') {
return loginDto;
} else {
return this.loginService.getone();
}
}
警告
如果不想验证可以不加 Dto 类型,直接返回 any 即可