
注意: 此处文档只起配合作用,为了您的身心愉悦,请看 B 站视频教程,不要直接略过视频直接看这个文档,这样你将什么都看不到
另,本人在找工作中,希望能有远程工作匹配(无法去外地),有需要的老板可以看一下我的个人介绍: https://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自定义的matadata数据(通过上面的装饰器定义)CoreModule注册管道时定义)与matadata默认的序列化拦截器是无法对分页数据进行处理的,所以自定义的全局序列化拦截器类重写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和ControllerEntity和ControllerService以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.tssrc/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; } 1 pincmancc 2023-01-06 08:59:26 +08:00 。。。 |