共计 4217 个字符,预计需要花费 11 分钟才能阅读完成。
- 视频地址: https://www.bilibili.com/vide…
有问题请扫描视频中 qq 群二维码交换
另,自己在找工作中,心愿能有近程工作匹配(无奈去当地),有须要的老板能够看一下我的集体介绍:pincman.com/about
学习指标
- 全局主动数据验证管道
- 全局数据序列化拦截器
- 全局异样解决过滤器
文件构造
本节内容次要聚焦于CoreModule
src/core
├── constants.ts
├── core.module.ts
├── decorators
│ ├── dto-validation.decorator.ts
│ └── index.ts
├── helpers.ts
├── index.ts
├── providers
│ ├── app.filter.ts
│ ├── app.interceptor.ts
│ ├── app.pipe.ts
│ └── index.ts
└── types.ts
利用编码
本节中用到一个新的 Typescript
知识点 - 自定义装璜器和matedata
, 具体应用请查看我写的一篇相干文章
装璜器
增加一个用于为 Dto
结构 metadata
数据的装璜器
// src/core/decorators/dto-validation.decorator.ts
export const DtoValidation = (
options?: ValidatorOptions & {transformOptions?: ClassTransformOptions;} & {type?: Paramtype},
) => SetMetadata(DTO_VALIDATION_OPTIONS, options ?? {});
验证管道
自定义一个全局的验证管道 (继承自Nestjs
自带的 ValidationPipe
管道)
代码: src/core/providers/app.pipe.ts
大抵验证流程如下
- 获取要验证的 dto 类
- 获取
Dto
自定义的matadata
数据(通过下面的装璜器定义) - 合并默认验证选项 (通过在
CoreModule
注册管道时定义)与matadata
- 依据 DTO 类上设置的 type 来设置以后的 DTO 申请类型(‘body’ | ‘query’ | ‘param’ | ‘custom’)
- 如果被验证的 DTO 设置的申请类型与被验证的数据的申请类型不是同一种类型则跳过此管道
- 合并以后 transform 选项和自定义选项(验证后的数据应用 class-transfomer` 序列化)
- 如果 dto 类的中存在 transform 静态方法, 则返回调用进一步 transform 之后的后果
- 重置验证选项和 transform 选项为默认
序列化拦截器
默认的序列化拦截器是无奈对分页数据进行解决的, 所以自定义的全局序列化拦截器类重写 serialize
办法, 以便对分页数据进行拦挡并序列化
// src/core/providers/app.interceptor.ts
serialize(
response: PlainLiteralObject | Array<PlainLiteralObject>,
options: ClassTransformOptions,
): PlainLiteralObject | PlainLiteralObject[] {const isArray = Array.isArray(response);
if (!isObject(response) && !isArray) return response;
// 如果是响应数据是数组, 则遍历对每一项进行序列化
if (isArray) {return (response as PlainLiteralObject[]).map((item) =>
this.transformToPlain(item, options),
);
}
// 如果是分页数据, 则对 items 中的每一项进行序列化
if (
'meta' in response &&
'items' in response &&
Array.isArray(response.items)
) {
return {
...response,
items: (response.items as PlainLiteralObject[]).map((item) =>
this.transformToPlain(item, options),
),
};
}
// 如果响应是个对象则间接序列化
return this.transformToPlain(response, options);
}
异样解决过滤器
Typeorm 在找不到模型数据时会抛出 EntityNotFound
的异样, 而此异样不会被捕捉进行解决, 以至于间接抛出 500
谬误, 个别在数据找不到时咱们须要抛出的是 404
异样, 所以须要定义一个全局异样解决的过滤器来进行捕捉并解决.
全局的异样解决过滤器继承自 Nestjs 自带的 BaseExceptionFilter
, 在自定义的类中定义一个对象属性, 并复写catch
办法以依据此属性中不同的异样进行判断解决
// src/core/providers/app.filter.ts
protected resExceptions: Array<
{class: Type<Error>; status?: number} | Type<Error>
> = [{class: EntityNotFoundError, status: HttpStatus.NOT_FOUND}];
catch(exception: T, host: ArgumentsHost) {...}
注册全局
在 CoreModule
中别离为全局的验证管道, 序列化拦截器和异样解决过滤器进行注册
在注册全局管道验证时传入默认参数
// src/core/core.module.ts
providers: [
{
provide: APP_PIPE,
useFactory: () =>
new AppPipe({
transform: true,
forbidUnknownValues: true,
validationError: {target: false},
}),
},
{
provide: APP_FILTER,
useClass: AppFilter,
},
{
provide: APP_INTERCEPTOR,
useClass: AppIntercepter,
},
],
})
逻辑代码
- 对于验证器须要批改
Dto
和Controller
- 对于拦截器须要批改
Entity
和Controller
- 对于过滤器须要批改
Service
主动序列化
以 PostEntity
为例, 比方在显示文章列表数据的时候为了缩小数据量不须要显示 body
内容, 而独自拜访一篇文章的时候则须要, 这时候能够增加增加一个序列化组 post-detail
, 而为了确定每个模型的字段在读取数据时只显示咱们须要的, 所以在类前增加一个@Exclude
装璜器
对于对象类型须要通过
@Type
装璜器的字段本义
示例
// src/modules/content/entities/post.entity.ts
...
@Expose()
@Type(() => Date)
@CreateDateColumn({comment: '创立工夫',})
createdAt!: Date;
@Expose()
@Type(() => CategoryEntity)
@ManyToMany((type) => CategoryEntity, (category) => category.posts, {cascade: true,})
@JoinTable()
categories!: CategoryEntity[];
@Expose({groups: ['post-detail'] })
@Column({comment: '文章内容', type: 'longtext'})
body!: string;
而后能够在在控制器中针对有非凡配置的序列化增加 @SerializeOptions
装璜器, 如序列化组
示例
// src/modules/content/controllers/post.controller.ts
...
@Get(':post')
@SerializeOptions({groups: ['post-detail'] })
async show(@Param('post', new ParseUUIDEntityPipe(PostEntity))
post: string,
) {return this.postService.detail(post);
}
主动验证
为了代码简洁, 把所有针对同一模型的 DTO
类全副放入一个文件, 于是有了以下 2 个 dto
文件
src/modules/content/dtos/category.dto.ts
src/modules/content/dtos/post.dto.ts
为 dto
文件中须要传入自定义验证参数的类增加 @DtoValidation
装璜器, 比方@DtoValidation({groups: ['create'] })
留神的是默认的 paramType
为body
, 所以对于query
, 须要额定加上type: 'query'
示例
// src/modules/content/dtos/category.dto.ts
@Injectable()
@DtoValidation({type: 'query'})
export class QueryCategoryDto implements PaginateDto {...}
当初能够在控制器中删除所有的 new ValidatePipe(...)
代码了, 因为全局验证管道会自行处理
主动解决异样
当初把服务中的 findOne
等查问全副改成 findOneOrFail
等, 把抛出的 NotFoundError
这些异样去除就能够在 typeorm 抛出默认的 EntityNotFound
异样时就会响应404
示例
// src/modules/content/services/post.service.ts
async findOne(id: string) {const query = await this.getItemQuery();
const item = await query.where('post.id = :id', { id}).getOne();
if (!item)
throw new EntityNotFoundError(PostEntity, `Post ${id} not exists!`);
return item;
}