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

相关推荐
挽安6211 小时前
若依微服务 Nacos 2.x 控制台配置列表为空?一个 tenant_id 字段引发的血案
后端
周杰伦fans1 小时前
ASP.NET Core Identity 从入门到实战:常见问题与解决方案
后端·asp.net
n8n2 小时前
Spring AI 三层架构详解:ChatModel → ChatClient → Controller,同步调用 vs 流式调用(SSE)
后端
小傅哥2 小时前
Java + DDD,1:1 复刻 Deepseek Harness 项目
前端·后端·ai编程
Lambert2812 小时前
AgentScope Java 从零(03):Agent 的记性默认全开,我在第 50 轮翻了车
后端·aigc
积硅步致千里2 小时前
Word 里的 .wmf 其实是 EMF:一次 metafile 转 PNG 排障
前端·后端
爱读源码的大都督2 小时前
DeepSeek面试官问:生产RAG系统回答不准确,该如何定位和优化?这样回答,能让面试官当场给你Offer!
java·后端·python
杨运交3 小时前
[069][公共模块]Spring Boot 全局异常处理与参数校验实战(下):校验异常精细化处理与 WebFlux 适配
java·spring boot·后端
Andya_net3 小时前
Spring Boot | 条件注解完全指南:从 @Conditional 到 @ConditionalOnExpression 的原理、实践与避坑
spring boot·后端·python