nest(3)- filter和Interceptor

filter和Interceptor

解释

🧠 Interceptor vs Filter:一张表讲清楚

Interceptor Filter
捕获什么 ✅ 正常返回值 ❌ 抛出的异常
触发条件 Controller 正常 return Service/Controller throw
改造方向 给返回值包一层 {code,msg,data} 把异常转成 {code,msg,data}
NestJS 接口 implements NestInterceptor implements ExceptionFilter
注册方式 app.useGlobalInterceptors(...) app.useGlobalFilters(...)
类比 工厂质检员(产品好的包装一下) 保安(有人闹事就控制住)

📌 完整执行流程图

kotlin 复制代码
HTTP 请求进来
    │
    ▼
  Guard(鉴权)
    │
    ▼
  Pipe(参数校验)
    │
    ▼
  Controller / Service
    │
    ├─ return ✅ ──▶ TransformInterceptor.pipe(map) ──▶ {code:0, msg:"ok", data:xxx}
    │
    └─ throw ❌ ──▶ AllExceptionsFilter ──▶ {code:4xx, msg:"xxx", data:null}

前端现在可以统一写:

js 复制代码
// axios 响应拦截器
response => {
  if (response.data.code === 0) {
    return response.data.data;   // 成功:取 data
  } else {
    throw new Error(response.data.msg);  // 失败:统一抛
  }
}

AOP 日志

✅ 日志全链路搞定!.log 文件自动生成

📁 生成的文件

lua 复制代码
nest-1/
└── logs/
    └── app-2026-09-09.log    ← 每天自动切分

📋 日志文件内容一览

ini 复制代码
[2026-09-09 06:19:57] [INFO] → GET /role/1 params={"id":"1"}
[2026-09-09 06:19:57] [INFO] ← GET /role/1 ✅ 9ms | return={"code":0,"msg":"ok","data":{"id":1,"name":"admin"}}
[2026-09-09 06:19:57] [INFO] → POST /user body={"name":"Dave","age":28,"roleId":1}
[2026-09-09 06:19:57] [INFO] ← POST /user ✅ 33ms | return={"code":0,"msg":"ok","data":{"id":7,"name":"Dave","age":28}}
[2026-09-09 06:19:57] [INFO] → POST /user body={"name":"X","age":99,"roleId":999}
[2026-09-09 06:19:57] [ERROR] ← POST /user ❌ 5ms | Role #999 does not exist

🔑 每个字段的含义

字段 示例 说明
[2026-09-09 06:19:57] 时间戳 精确到秒
[INFO] / [ERROR] 日志级别 成功=绿色 INFO,失败=红色 ERROR
→ 请求方向 请求进来
← 响应方向 响应出去
params=... / body=... 请求参数 URL 参数 + body(自动脱敏 password)
✅ 9ms 成功耗时 毫秒级
❌ 5ms 失败耗时 异常也会记录耗时
return=... 返回值预览 最多 100 字符

🧠 LoggingInterceptor 的执行逻辑(RxJS 拆解)

ts 复制代码
return next.handle().pipe(
  // tap = "偷看一眼",数据流继续传,不改造
  tap((data) => {
    // 成功:算耗时 + 记录
    log(`← ${method} ${url} ✅ ${duration}ms | return=${preview}`);
  }),
  // catchError = "抓异常",记录后重新抛出去
  catchError((err) => {
    log(`← ${method} ${url} ❌ ${duration}ms | ${err.message}`, 'ERROR');
    return throwError(() => err);  // 继续抛,让 Filter 处理
  }),
);
RxJS 操作符 作用 类比
tap 副作用,不改造数据 工厂质检员看了一眼产品,没动手
map 改造数据 工厂质检员把产品包装好
catchError 捕获异常 工厂质检员发现次品,记录后扔回流水线

🏗️ 现在你的项目有 3 个 Interceptor + 1 个 Filter

css 复制代码
请求进来
    │
    ▼
┌───────────────────────────────────────┐
│ LoggingInterceptor(外层)            │  → 记录请求开始
│   tap + catchError                    │  ← 记录响应/异常+耗时
└───────────────┬───────────────────────┘
                ▼
┌───────────────────────────────────────┐
│ TransformInterceptor(内层)          │  → 不做事
│   pipe(map)                           │  ← 包一层 {code:0,msg:"ok",data}
└───────────────┬───────────────────────┘
                ▼
         Controller / Service
                │
     ┌──────────┴──────────┐
     ▼                     ▼
  return ✅              throw ❌
     │                     │
     │              ┌──────┴──────┐
     │              │ AllExceptionsFilter
     │              │ 格式化 + 错误日志
     │              └─────────────┘
     ▼                     ▼
  HTTP 200            HTTP 4xx/5xx
  {code:0,...}         {code:400,...}

📌 一句话总结

LoggingInterceptor 利用 RxJS 的 tap + catchError,不改造数据,只做"副作用"(写日志)。 和 TransformInterceptor(改造数据)配合,一个记录、一个格式化,互不干扰。日志同时输出到控制台 (彩色)和 logs/app-YYYY-MM-DD.log 文件,每天自动切分------生产环境可以加个 logrotate 定时归档,目前体量小直接用就行。

相关推荐
EatFan6 小时前
Spring Boot 4 落地观察:从 yudao-cloud、matecloud、JPower 看国产脚手架的升级路线与迁移清单
java·spring boot·后端·spring cloud·微服务·后端开发·jdk 21
波力海苔夹心脆6758 小时前
C# 序列化与反序列化详解:System.Text.Json、Newtonsoft.Json、XmlSerializer 用法、特性选项与安全实践
经验分享·后端·c#·json·.net
JavaGuide9 小时前
NVIDIA 又开源了!这次给 AI Agent 加上权限管控
前端·后端
excel9 小时前
prisma 如何处理数据库竞态
前端·数据库·后端
IT_陈寒11 小时前
Java线程池这破玩意,差点让我周末加班排查到凌晨
前端·人工智能·后端
弈栈录12 小时前
Java AI 应用的异步化与高并发设计
java·后端·架构
凤山老林12 小时前
Spring Boot 集成 iText 7 实现动态 PDF 生成与电子签章:合同、报表场景实战
spring boot·后端·pdf·itext7·电子签章
孙启超13 小时前
【AI开发之Rust】第 19 课:UniFFI 导出核心能力
开发语言·后端·rust
ServBay13 小时前
基于Jev的浏览器Agent插件狂揽 21k star,3分钟教你解放双手
后端·aigc·ai编程