管道
管道是一个使用@Injectable()
装饰器注解的类,它实现了PipeTransform
接口
管道它要么不变地返回值,要么抛出一个异常
。
管道有两种典型的用例
转换:将输入数据转换为所需的形式(例如:从字符串到整数)
验证:评估输入数据,如果有效则简单的将其原样传递,否则抛出异常
在这两种情况下,管道都作用于控制器路由处理器正在处理的参数上.
Nest 在方法被调用之前插入管道
管道接收传递给方法的参数并对其进行操作.
任何转换或者验证操作都在此时发生,然后路由处理器使用任何(可能)已转换的参数被调用
Nest 自带了许多内置的管道,你可以直接使用
你也可以构建自己的定制管道
内建管道
ValidationPipe:验证输入数据,确保输入数据符合预期格式
ParseIntPipe:将输入数据转换为整数
ParseBoolPipe:将输入数据转换为布尔值
ParseFloatPipe:将输入数据转换为浮点数
ParseUUIDPipe:将输入数据转换为 UUID
ParseArrayPipe:将输入数据转换为数组
ParseEnumPipe:将输入数据转换为枚举
DefaultValuePipe:为输入数据设置默认值
ParseFilePipe:将输入数据转换为文件
新建一个文件夹起名 pipe ,新建一个文件起名 validation.pipe.ts
ts
import {
PipeTransform,
Injectable,
ArgumentMetadata,
HttpException,
} from "@nestjs/common";
@Injectable()
export class ValidationPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
console.log("value", value);
console.log(metadata);
if (value.length != 11) {
throw new HttpException("手机号格式不正确", 401);
}
return value;
}
}
- 使用
ts
import { ValidationPipe } from '../pipe/validate.pipe';
@Post(':id')
async getGuandao2(
@Param('id') id: number,
@Body('phone', ValidationPipe) phone: number,
): Promise<string> {
console.log('见来了啊哈哈哈');
return 'guandao';
}
通用验证器
安装
bash
pnpm i class-validator class-transformer -S
写 dto(验证规则)
新建一个文件夹起名 dto,新建一个文件起名 user-create.dto.ts
bash
import { IsInt, IsString } from 'class-validator';
export class UserCreateDto {
@IsString({ message: '用户名必须是字符串' })
username: string;
@IsString({ message: '密码必须是字符串' })
password: string;
@IsString({ message: '邮箱必须是字符串' })
email: string;
@IsInt({ message: '年龄必须是整数' })
age: number;
message: string;
}
创建一个管道
- (1)抛出异常版本
新建一个文件夹起名 pipe,新建一个文件起名 user-create.pipe.ts
ts
import {
PipeTransform,
Injectable,
ArgumentMetadata,
HttpException,
} from "@nestjs/common";
import { plainToInstance } from "class-transformer";
import { validate } from "class-validator";
@Injectable()
export class ValidationPipe2 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) {
console.log(errors);
// 第一种只返回第一个
if (errors.length == 1) {
for (let arr in errors[0].constraints) {
throw new HttpException(`${errors[0].constraints[arr]}`, 400);
}
}
// 第二种返回所有
let result = [];
errors.forEach((item) => {
for (let arr in item.constraints) {
result.push({
message: item.constraints[arr],
field: item.property,
});
}
});
throw new HttpException(JSON.stringify(result), 400);
}
// 自定义验证
if (value.message != "你好") {
throw new HttpException("message必须是你好", 400);
}
return object;
}
private toValidate(metatype: Function): boolean {
// 白名单
const types: Function[] = [String, Boolean, Number, Array, Object];
return !types.includes(metatype);
}
}
- (2) 不抛出异常版本
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 { ValidationPipe2 } from '../pipe/validate2.pipe';
@Post()
async getGuandao2(
@Body(ValidationPipe2) UserCreateDto: UserCreateDto,
): Promise<string> {
if(UserCreateDto.status == "error"){
return UserCreateDto
}else{
return this.loginService.getone();
}
}
全局管道(全局使用)
- app.module.ts
ts
import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common";
import { CoreModule } from "./core/core.module";
import { TaskModule } from "./task/task.module";
import { APP_FILTER, APP_PIPE } from "@nestjs/core";
import { HttpExceptionFilter } from "./task/filter/HttpException.filter";
import { ValidationPipe2 } from "./task/pipe/validate2.pipe";
@Module({
imports: [CoreModule, TaskModule],
controllers: [],
providers: [
{
provide: APP_FILTER,
useClass: HttpExceptionFilter,
},
{
provide: APP_PIPE,
useClass: ValidationPipe2,
},
],
})
// 模块实现 NestModule 接口
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
// 限定路由
consumer.apply().forRoutes("*");
}
}