
另,本人在找工作中,希望能有远程工作匹配(无法去外地),有需要的老板可以看一下我的个人介绍: https://pincman.com/about
sanitize-html对文章内容进行防注入攻击处理html标签,防注入攻击~ pnpm add nestjs-typeorm-paginate sanitize-html deepmerge && pnpm add @types/sanitize-html -D 创建文件
cd src/modules/content && \ mkdir subscribers && \ touch dtos/query-category.dto.ts \ dtos/query-post.dto.ts \ subscribers/post.subscriber.ts \ subscribers/index.ts \ services/sanitize.service.ts \ && cd ../../../ 与上一节一样,这一节的新增和修改集中于ContentModule
src/modules/content ├── constants.ts ├── content.module.ts ├── controllers │ ├── category.controller.ts │ ├── comment.controller.ts │ ├── index.ts │ └── post.controller.ts ├── dtos │ ├── create-category.dto.ts │ ├── create-comment.dto.ts │ ├── create-post.dto.ts │ ├── index.ts │ ├── query-category.dto.ts │ ├── query-post.dto.ts │ ├── update-category.dto.ts │ └── update-post.dto.ts ├── entities │ ├── category.entity.ts │ ├── comment.entity.ts │ ├── index.ts │ └── post.entity.ts ├── repositories │ ├── category.repository.ts │ ├── comment.repository.ts │ ├── index.ts │ └── post.repository.ts ├── services │ ├── category.service.ts │ ├── comment.service.ts │ ├── index.ts │ ├── post.service.ts │ └── sanitize.service.ts └── subscribers ├── index.ts └── post.subscriber.ts 这节多了一个新的概念,即subscriber,具体请查阅typeorm文档,当然你也可以在模型中使用事件处理函数,效果没差别
CategoryEntity代码:src/modules/content/entities/category.entity.ts
order字段用于排序level属性(虚拟字段)用于在打平树形数据的时候添加当前项的等级代码: src/modules/content/entities/post.entity.ts
type字段的类型用enum枚举来设置,首先需要定义一个PostBodyType的enum类型,可以添加一个constants.ts文件来统一定义这些enum和常量
publishedAt字段用于控制发布时间和发布状态 type字段用于设置发布类型customOrder字段用于自定义排序CategoryRepository代码: src/modules/content/repositories/category.repository.ts
因为CategoryRepository继承自TreeRepository,所以我们在typeorm源码中找到这个类,并对部分方法进行覆盖,如此我们就可以对树形分类进行排序,覆盖的方法如下
当然后面会讲到更加深入的再次封装,此处暂时先这么用
findRoots 为根分类列表查询添加排序createDescendantsQueryBuilder 为子孙分类查询器添加排序createAncestorsQueryBuilder 为祖先分类查询器添加排序新增QueryCategoryDto和QueryPostDto用于查询分类和文章时进行分页以及过滤数据和设置排序类型等
在添加DTO之前,现在添加几个数据转义函数,以便把请求中的字符串改成需要的数据类型
// src/core/helpers.ts // 用于请求验证中的 number 数据转义 export function tNumber(value?: string | number): string |number | undefined // 用于请求验证中的 boolean 数据转义 export function tBoolean(value?: string | boolean): string |boolean | undefined // 用于请求验证中转义 null export function tNull(value?: string | null): string | null | undefined 修改create-category.dto.ts和create-comment.dto.ts的parent字段的@Transform装饰器
export class CreateCategoryDto { ... @Transform(({ value }) => tNull(value)) parent?: string; } 添加一个通用的DTO接口类型
// src/core/types.ts // 分页验证 DTO 接口 export interface PaginateDto { page: number; limit: number; } QueryCategoryDto代码: src/modules/content/dtos/query-category.dto.ts
page属性设置当前分页limit属性设置每页数据量QueryPostDto除了与QueryCateogryDto一样的分页属性外,其它属性如下
orderBy用于设置排序类型isPublished根据发布状态过滤文章category过滤出一下分类及其子孙分类下的文章orderBy字段是一个enum类型的字段,它的可取值如下
CREATED: 根据创建时间降序UPDATED: 根据更新时间降序PUBLISHED: 根据发布时间降序COMMENTCOUNT: 根据评论数量降序CUSTOM: 根据自定义的order字段升序SanitizeService代码: src/modules/content/services/sanitize.service.ts
此服务类用于clean html
saniize方法用于对 HTML 数据进行防注入处理
CategoryService代码:src/modules/content/services/category.service.ts
添加一个辅助函数,用于对打平后的树形数据进行分页
// src/core/helpers.ts export function manualPaginate<T extends ObjectLiteral>( { page, limit }: PaginateDto, data: T[], ): Pagination<T> 新增paginate(query: QueryCategoryDto)方法用于处理分页
async paginate(query: QueryCategoryDto) { // 获取树形数据 const tree = await this.findTrees(); // 打平树形数据 const list = await this.categoryRepository.toFlatTrees(tree); // 调用手动分页函数进行分页 return manualPaginate(query, list); } PostService代码:src/modules/content/services/post.service.ts
getListQuery: 用于构建过滤与排序以及通过分类查询文章数据等功能的query构建器paginate: 调用getListQuery生成query,并作为nestjs-typeorm-paginate的 paginate的参数对数据进行分页async paginate(params: FindParams, options: IPaginationOptions) { const query = await this.getListQuery(params); return paginate<PostEntity>(query, options); } PostSubscriber代码: src/modules/content/subscribers/post.subscriber.ts
beforeInsert(插入数据前事件): 如果在添加文章的同时发布文章,则设置当前时间为发布时间beforeUpdate(更新数据前事件): 更改发布状态会同时更新发布时间的值,如果文章更新为未发布状态,则把发布时间设置为 nullafterLoad(加载数据后事件): 对 HTML 类型的文章内容进行去标签处理防止注入攻击一个需要注意的点是需要在subcriber类的构造函数中注入Connection才能获取链接
constructor( connection: Connection, protected sanitizeService: SanitizeService, ) { connection.subscribers.push(this); } 把订阅者注册成服务后,由于在构造函数中注入了connection这个连接对象,所以typeorm会自动把它加载到这个默认连接的subscribers配置中
// src/modules/content/subscribers/post.subscriber.ts import * as SubscriberMaps from './subscribers'; const subscribers = Object.values(SubscriberMaps); @Module({ .... providers: [...subscribers, ...dtos, ...services], }) CategoryController代码: src/modules/content/controllers/category.controller.ts
list: 通过分页来查找扁平化的分类列表index: 把 url 设置成 @Get('tree') @Get() // 分页查询 async list( @Query( new ValidationPipe({ transform: true, forbidUnknownValues: true, validationError: { target: false }, }), ) query: QueryCategoryDto, ) { return this.categoryService.paginate(query); } // 查询树形分类 @Get('tree') async index() { return this.categoryService.findTrees(); } PostController代码: src/modules/content/controllers/post.controller.ts
修改index方法用于分页查询
// 通过分页查询数据 async index( @Query( new ValidationPipe({ transform: true, forbidUnknownValues: true, validationError: { target: false }, }), ) { page, limit, ...params }: QueryPostDto, ) { return this.postService.paginate(params, { page, limit }); } 1 putaozhenhaochi 2022-07-10 10:10:34 +08:00 via Android 大哥 不在这搞 seo 行吗 |
2 golangLover 2022-07-10 10:17:54 +08:00 via Android 支持一下,辛苦了 |
3 dinjufen 2022-07-10 11:10:46 +08:00 网站订阅有点贵,但是内容又不多 |
5 Kipp 2022-07-10 11:59:30 +08:00 昨晚看还是全免费,今早就订阅者免费了,建议发推广 |
7 chenzhe 2022-07-10 23:38:53 +08:00 via iPhone 昨晚点开看了一下 说实在的,对于初学者来说,讲得太浅显,对于真正后端开发的熟手来说,好像又犯不着看这样的教程。 感觉定位有点儿尴尬。 而且这样的帖子明显是推广贴,应该换一个节点发。 真的做付费视频,希望能够讲的更加详细一些,带着新手一步一步把 nestjs 吃透。 数据库操作这边,选 mongoose 或者 typeorm 详细的讲一下。 |
8 lichnow OP @chenzhe 视频才更到 5 啊,后面还有 30 集,很多东西得一步步深入不是?像 typeorm 5-10 是专门讲解的,用户系统 11-15 讲解,16-18 就讲 RABC,不可能几集就能全部讲完啊 |
10 chenzhe 2022-07-11 12:36:31 +08:00 @lichnow 的确才更新了一点儿,但是看前五集就能知道你这个视频并不是针对初学者的。至于跟着视频做,看了视频,连为啥这么做都不知道。 |
11 lichnow OP @chenzhe 光看视频肯定不行,需要跟着源代码一步步实现,我每一集的源代码单独一个包,然后实现过程中有问题(比如不知道为啥这么做)可以群里或者问答频道提问,我会耐心解答 |