一、各组件命名原因与设计目标
组件名 | 命名原因 | 设计目标 | 作用位置 |
---|---|---|---|
Middleware(中间件) | 源自 Express,意为“中间层处理者” | 请求预处理(如日志、跨域、session) | 请求最外层,最先执行 |
Guard(守卫) | 比喻为“守门人” | 权限控制、访问鉴权 | Middleware 之后,Controller 之前 |
Interceptor(拦截器) | 类比 Spring 中的拦截器 | 请求/响应前后扩展逻辑(如日志、缓存、数据格式化) | Guard 之后,Pipe 之前;也可在响应后处理 |
Pipe(管道) | 比喻为“数据通道” | 参数验证与转换(如 DTO 校验、类型转换) | Interceptor 之后,Controller 方法之前 |
Filter(过滤器) | 类比 Java Servlet 中的过滤器 | 异常捕获与统一响应格式处理 | 整个流程中任何位置抛出的异常都会被捕获 |
DTO(数据传输对象) | 经典设计模式名 | 明确接口数据结构与类型,提升可读性与可维护性 | 与 Pipe 配合使用,用于参数校验 |
二、执行顺序
请求 → Middleware → Guard → Interceptor(before)→ Pipe → Controller → Service → Controller → Interceptor(after)→ Filter(异常时)→ 响应
三、各个组件详解
下面把每一个组件都拆开来讲,为什么叫这个名字、设计目标是什么、在请求链里到底扮演什么角色。
1. Middleware(中间件)
项目 | 说明 |
---|---|
为什么叫中间件 | 字面意思:夹在「HTTP 层」与「业务层」之间的件;Express 先叫响的名字,Nest 拿来直接复用。 |
设计目标 | 只做「请求级」的通用杂事:CORS、日志、 helmet、session、raw-body 解析等,与业务 0 耦合。 |
在链上的位置 | 最先跑,甚至还没实例化 Controller;可以 next() 也可以提前结束响应。 |
典型场景 | app.use(logger) 、app.use(cors()) 。 |
✨一句话:Middleware 是「门卫+保洁」,只认请求,不认路由。
2. Guard(守卫)
项目 | 说明 |
---|---|
为什么叫守卫 | 借游戏术语:关卡守门人,过不去就 403/401。 |
设计目标 | 把「能不能进」与「进去干什么」彻底分开;集中处理鉴权、角色、许可证。 |
在链上的位置 | Middleware 之后,Pipe 之前;能拿到反射出来的自定义装饰器(如 @Roles() )。 |
典型场景 | @UseGuards(JwtAuthGuard, RolesGuard) 。 |
✨一句话:Guard 只回答「你行不行」,不回答「你对不对」。
3. Interceptor(拦截器)
项目 | 说明 |
---|---|
为什么叫拦截器 | Spring 借来的名字:可以「拦」住请求/响应,再包一层逻辑。 |
设计目标 | AOP 精髓——横向扩展;日志、缓存、数据脱敏、统一封装返回值、RxJS 流变换。 |
在链上的位置 | 请求方向:Guard → Interceptor → Pipe → Controller;响应方向:Controller → Interceptor → 客户端。 |
典型场景 | 把 return {data} 自动包成 {code:0,data,msg:'ok'} ;ETag 缓存。 |
✨一句话:Interceptor 是「收发室」,进出都要过它手,可以偷看也可以改装。
4. Pipe(管道)
项目 | 说明 |
---|---|
为什么叫管道 | Unix 管道思想:数据从一个命令流向下一个命令;这里指「参数」被一步步转换/校验。 |
设计目标 | 把「外部不可信数据」变成「内部可靠数据」;校验、转型、设置默认值。 |
在链上的位置 | 紧贴着要进 Controller 方法的参数;每个参数可以专属一个 Pipe。 |
典型场景 | @Body() createUserDto: CreateUserDto 自动做 class-validator 校验;把字符串 id 转成 ObjectId。 |
✨一句话:Pipe 是「安检仪」,过不去直接 400,过去了就是干净数据。
5. Filter(异常过滤器)
项目 | 说明 |
---|---|
为什么叫过滤器 | 借 Servlet 的 Filter 概念:把异常「过滤」掉,换成友好响应。 |
设计目标 | 统一错误格式、隐藏敏感堆栈、区分环境、写日志、发告警。 |
在链上的位置 | 整个链路任何地方抛错,都会被最近的 Filter 捕获;不抛错它就不出现。 |
典型场景 | 把 MongoError 转换成 409 业务码;把 HttpException 包成 {code,msg} JSON。 |
✨一句话:Filter 是「救火队」,只负责「烧起来后怎么收场」。
6. DTO(数据传输对象)
项目 | 说明 |
---|---|
为什么叫 DTO | 经典《POEAA》模式名:专为网络传输而生的对象,与数据库实体解耦。 |
设计目标 | 让接口形状显性化、可复用、可组合;配合 Pipe 做校验;屏蔽内部字段。 |
在链上的位置 | 本身不是可执行组件,而是 Pipe/Controller 的「靶子」;通过 class-validator 装饰器描述规则。 |
典型场景 | CreateCatDto 、UpdateCatDto 分开控制必填字段;把密码字段排除在返回层(搭配 Exclude() )。 |
✨一句话:DTO 是「合同」,先签字再开工,防止扯皮。
7. 小结(速记表)
组件 | 关键词 | 一句话记忆 |
---|---|---|
Middleware | 请求级杂活 | 门卫保洁 |
Guard | 鉴权 | 守门员 |
Interceptor | 横向扩展 | 收发室 |
Pipe | 参数校验/转换 | 安检仪 |
Filter | 异常格式化 | 救火队 |
DTO | 传输形状 | 合同 |
这些名字不是拍脑袋起的,而是把现实世界的角色映射到代码世界,让开发者一眼就能猜到它该干什么;再通过 AOP+IoC 把「什么时候干」和「怎么干」解耦,最终达到「业务代码里只有业务」的目的。
参考
- https://juejin.cn/post/7553830228104888374
- https://onfuns.com/nestjs-design-philosophy-and-fundamental-concepts
- https://blog.csdn.net/qq_53673551/article/details/146186714
- https://blog.csdn.net/lin_fightin/article/details/140892354
- https://cloud.tencent.com/developer/article/2169295
- https://zhuanlan.zhihu.com/p/378234302
- https://stackoverflow.com/questions/58582886/what-is-the-difference-between-useguards-and-middleware-in-nestjs