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 定时归档,目前体量小直接用就行。

相关推荐
专注仿真3 分钟前
ThinkPHP6 API接口开发实例:从零搭建RESTful风格接口
后端·php·restful
开心猴爷16 分钟前
应对iOS审核4.3:可以使用 IpaGuard 来给属性名混淆的技术细节与平衡之道
后端·ios
1m5555555518 分钟前
RAG 工程实战(五):正确知识已经进入 Prompt,为什么大模型还是会答错?
后端·面试
易小尊19 分钟前
我的模型在 65% 以上时准得可怕,在 50% 时像在掷硬币
后端
用户27886072856719 分钟前
fallback 会盖掉平台提示词:system、空数组和 systemAppend 的差别
前端·后端
iOS开发上架哦20 分钟前
iOS应用耗电测试:使用Energy Log、Keymob和Instruments进行性能监控与能源消耗分析
后端·ios
掘金挖土33 分钟前
前端手摸手跑路之 AI 应用开发(十一)
前端·后端
用户98883218717434 分钟前
从“限流数字”到可维护策略:ASP.NET Core 固定窗口限流的落地边界
后端
IT_陈寒35 分钟前
Vue动态组件缓存的坑你踩过了吗?
前端·人工智能·后端
雾行0x001 小时前
粗排:如何在毫秒级筛出值得精排的内容?
后端·架构