实战使用 NestJS 搭建一个 Admin 后台服务 - 01. 项目搭建、配置文件和路由
实战使用 NestJS 搭建一个 Admin 后台服务 - 02. typeorm 操作 mysql&基础 crud
一、DTO参数校验
- 同样的先安装依赖
bash
yarn add class-validator class-transformer
1、使用内置pipe 或者 自定义pipe
-
一般使用内置管道即可,当然有自定义处理需求的也可以自定义
-
自定义pipe, 推荐查看官网 docs.nestjs.cn/10/pipes?id...
ts
// src/pipe/validation.pipe.ts
import {
ArgumentMetadata,
Injectable,
PipeTransform,
BadRequestException,
} from '@nestjs/common';
import { validate } from 'class-validator';
import { plainToInstance } from 'class-transformer';
import { Logger } from '../utils/log4js';
@Injectable()
export class MyValidationPipe implements PipeTransform {
async transform(value: any, { metatype }: ArgumentMetadata) {
if (!metatype || !this.toValidate(metatype)) {
// 如果没有传入验证规则,则不验证,直接返回数据
return value;
}
// 将对象转换为 Class 来验证
const object = plainToInstance(metatype, value);
const errors = await validate(object);
if (errors.length > 0) {
const msg = Object.values(errors[0].constraints)[0]; // 只需要取第一个错误信息并返回即可
Logger.error(`Validation failed: ${msg}`);
throw new BadRequestException(`Validation failed: ${msg}`);
}
return value;
}
private toValidate(metatype: any): boolean {
const types: any[] = [String, Boolean, Number, Array, Object];
return !types.includes(metatype);
}
}
- 使用内置的ValidationPipe,官网说明 docs.nestjs.cn/8/technique...
2、修改dto
- 修改 create-user.dto.ts 不过 CreateUserDto继承了 user.entity.ts 所以直接修改 user.entity.ts 较为方便
ts
import { IsNotEmpty } from 'class-validator';
import {
Entity,
Column,
PrimaryGeneratedColumn,
CreateDateColumn,
UpdateDateColumn,
} from 'typeorm';
@Entity()
export class User {
/** 每个实体必须至少有一个主列 */
@PrimaryGeneratedColumn()
id: number;
/** @CreateDateColumn 是一个特殊列,自动为实体插入日期。无需设置此列,该值将自动设置。 */
@CreateDateColumn({
type: 'timestamp',
nullable: false,
name: 'create_time',
comment: '创建时间',
})
createTime: Date;
/** @UpdateDateColumn 是一个特殊列,在每次调用实体管理器或存储库的save时,自动更新实体日期。无需设置此列,该值将自动设置。 */
@UpdateDateColumn({
type: 'timestamp',
nullable: false,
name: 'update_time',
comment: '更新时间',
})
updateTime: Date;
@IsNotEmpty({
message: '用户名不能为空',
})
@Column({ unique: true, nullable: true, comment: '用户名' })
username: string;
@IsNotEmpty({
message: '邮箱不能为空',
})
@Column({ unique: true, nullable: true, comment: '邮箱' })
email: string;
@IsNotEmpty({
message: '手机号不能为空',
})
@Column({ unique: true, nullable: true, comment: '手机号' })
phone: string;
@Column({ nullable: true, comment: '头像' })
avatar: string;
@IsNotEmpty({
message: '密码不能为空',
})
@Column({ nullable: true, comment: '密码' })
password: string;
}
3、全局使用 or 局部使用
- 全局使用
- exceptionFactory 做了一定转化,如下处理后,message 返回的是字符串,且是第一个验证错误
- new ValidationPipe() 不对exceptionFactory 处理message 返回的是所有校验错误的字符串数组
ts
// main.ts
import { BadRequestException, ValidationPipe } from '@nestjs/common';
/** 全局绑定验证管道 或者自定义 new MyValidationPipe() */
app.useGlobalPipes(
new ValidationPipe({
exceptionFactory: (errors) => {
const firstError = errors[0];
const message =
firstError.constraints[Object.keys(firstError.constraints)[0]];
return new BadRequestException(message);
},
}),
);
- 局部使用, 在controller 层使用 users.controller.ts
ts
@UsePipes(new ValidationPipe()) // 或者自定义 new MyValidationPipe()
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
4、效果展示
- exceptionFactory 未转化
- exceptionFactory 转化
二、swagger接口文档生成
-
同样给出官网文档位置 docs.nestjs.cn/10/recipes?...
-
依赖引入
bash
yarn add @nestjs/swagger swagger-ui-express
- 修改main.ts
ts
const options = new DocumentBuilder()
.setTitle('Cats example')
.setDescription('The cats API description')
.setVersion('1.0')
.addTag('cats')
.build();
const document = SwaggerModule.createDocument(app, options);
SwaggerModule.setup('doc', app, document); // 使用doc 不使用api 做一定区分
- 访问http://localhost:3003/doc-json 查看 swagger json 数据
三、导入api fox
- apifox 自行下载
- 可以看到已经导入了,但body内相关参数并没有
- 接下来进行swagger 装饰器文档编写
四、编写swagger 装饰器文档
1、修改user.entity.ts
ts
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty } from 'class-validator';
import {
Entity,
Column,
PrimaryGeneratedColumn,
CreateDateColumn,
UpdateDateColumn,
} from 'typeorm';
@Entity()
export class User {
/** 每个实体必须至少有一个主列 */
@PrimaryGeneratedColumn()
id: number;
/** @CreateDateColumn 是一个特殊列,自动为实体插入日期。无需设置此列,该值将自动设置。 */
@CreateDateColumn({
type: 'timestamp',
nullable: false,
name: 'create_time',
comment: '创建时间',
})
createTime: Date;
/** @UpdateDateColumn 是一个特殊列,在每次调用实体管理器或存储库的save时,自动更新实体日期。无需设置此列,该值将自动设置。 */
@UpdateDateColumn({
type: 'timestamp',
nullable: false,
name: 'update_time',
comment: '更新时间',
})
updateTime: Date;
@IsNotEmpty({
message: '用户名不能为空',
})
@ApiProperty({ uniqueItems: true, description: '用户名' })
@Column({ unique: true, nullable: true, comment: '用户名' })
username: string;
@IsNotEmpty({
message: '邮箱不能为空',
})
@ApiProperty({ uniqueItems: true, description: '邮箱' })
@Column({ unique: true, nullable: true, comment: '邮箱' })
email: string;
@IsNotEmpty({
message: '手机号不能为空',
})
@ApiProperty({ uniqueItems: true, description: '手机号' })
@Column({ unique: true, nullable: true, comment: '手机号' })
phone: string;
@ApiProperty({ description: '头像' })
@Column({ nullable: true, comment: '头像' })
avatar: string;
@IsNotEmpty({
message: '密码不能为空',
})
@ApiProperty({ description: '密码' })
@Column({ nullable: true, comment: '密码' })
password: string;
}
2、重复 三、导入api fox
下一篇
本篇完成了参数校验的相关工作,下篇将进行jwt 的逻辑编写