nestjs学习 - 拦截器(intercept)

拦截器是使用 @Injectable() 装饰器注解的类。拦截器应该实现 NestInterceptor 接口。

一、它是什么

拦截器(Interceptor) 是一个基于 面向切面编程(AOP) 思想的强大功能。它允许你在请求到达控制器(Controller)之前或之后,以及响应返回给客户端之前,插入自定义逻辑。

白话:

从上图可以看出,拦截器就是可以在 到达请求前 和 请求返回结果后 进行拦截,做一些你想做的事情;

前端开发同学可以结合 axios 的拦截器理解,几乎就是同一个模式;

简单说:拦截器就是请求和响应路上的"把关人",能在不修改核心业务代码的情况下,统一处理一些公共逻辑。

在下文中主要关注它的使用场景;

在框架生命周期中,它的执行时机是:

请求进入 → 中间件 → 守卫 → 拦截器 → 管道 → 控制器 → 服务 → 拦截器 → 异常过滤器 → 服务器响应

二、使用方法

在使用拦截器之前,需了解 RxJS(响应式编程库) 的使用

它底层严重依赖 RxJS ,因为 intercept() 方法返回的是一个 Observable(可观察对象)。这意味着你需要对 RxJS 的操作符(如 map, tap, catchError 等)有一定了解。

1. 创建

统一响应数据格式demo:

typescript 复制代码
import { NestInterceptor, CallHandler, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
​
​
interface Data<T> {
  code: number;
  message: string;
  data: T;
}
​
/**
 * 响应拦截器
 * 用于处理响应数据
 * 可以用于处理响应数据,如添加响应头,添加响应体等
 */
@Injectable()
export class ResponseInterceptor<T> implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<Data<T>> {
    // ==========================
    // 【阶段 1:控制器执行之前】
    // ==========================
    // 这里的代码会立即同步执行。
    // 此时请求刚到达拦截器,还没进控制器。
    console.log('❤️ [之前] 请求已到达拦截器');
    const startTime = Date.now();
    
    // 可以在这里做:权限预检、记录开始时间、修改请求参数等。
    // 如果在这里直接 return 一个 Observable (例如 return of({error: 'blocked'})) 
    // 而不调用 next.handle(),控制器将永远不会执行(短路)。
​
    // 调用 next.handle() 启动控制器逻辑
    // 它返回一个 Observable,代表控制器未来的执行结果(流)
    const response$ = next.handle(); 
    
    // ==========================
    // 【阶段 2:控制器执行之后】
    // ==========================
    // 这里的代码不会立即执行!
    // 它们被注册为 RxJS 的"操作符",只有当控制器执行完毕并产生数据时,流才会流动到这里。
    return response$.pipe(
      map(data => {
        return {
          code: 200,
          message: 'success',
          data,
        };
      }),
    );
  }
}

2. 注册

有三种注册方式,作用范围依次扩大:

方法级别:

仅针对某个特定路由

less 复制代码
@Get('users')
@UseInterceptors(LoggingInterceptor)
findAll() {
  return this.userService.findAll();
}

控制器级别:

针对该控制器下的所有路由

less 复制代码
@Controller('users')
@UseInterceptors(LoggingInterceptor)
export class UsersController {
  // ...
}

全局级别:

针对整个应用的所有路由,在 main.ts 中注册:

ini 复制代码
const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(new LoggingInterceptor());
await app.listen(3000);

三、使用场景:

  1. 统一响应格式格式化(正常数据、错误数据)

  2. 响应缓存

    对于不经常变动的数据(如配置信息、列表页),可以在拦截器中检查缓存。

    • 如果缓存命中,直接 return of(cachedData),不调用 next.handle(),从而跳过控制器逻辑,极大提升性能。
    • 如果未命中,正常执行并写入缓存。
  3. 超时处理

    如果某个请求处理时间过长,可以强制中断。

    javascript 复制代码
    import { timeout } from 'rxjs/operators';
    ​
    // 在 intercept 方法中
    return next.handle().pipe(
      timeout(5000), // 5秒无响应则抛出异常
    );
  4. 数据序列化/脱敏

    在返回给用户之前,动态修改敏感字段。

    • 例如:将用户列表中的 password 字段移除,或将手机号中间四位替换为 ****。
    • 通过 map 操作符遍历返回数据并进行清洗。

四、总结:

  • 基于 AOP 思想,利用 RxJS 在请求/响应生命周期中插入逻辑的机制。
  • 它本质上是一个强大的"切面"工具,用于处理那些横跨整个应用程序的、与核心业务逻辑无关的公共关注点。

    它的精髓在于:你可以在不侵入、不修改任何一个现有控制器方法的情况下,为整个应用或特定接口批量添加上述各种功能。 这使得你的代码更加干净、可维护,并且这些横切关注点可以被轻松地复用和组合。

  • 统一返回格式、日志记录、性能监控、缓存、数据转换、超时控制。
  • 区别: 比中间件更灵活,能操作返回值;比守卫(Guard)更侧重于数据转换而非权限决策。
相关推荐
子兮曰2 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰2 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万3 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝3 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋3 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁3 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
汉堡大王95273 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大3 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师3 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端
沙蒿同学3 天前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端