Skip to content

NestJS 拦截器

基础语法

拦截器需实现 NestInterceptor 接口,核心方法是 intercept(context: ExecutionContext, next: CallHandler)

  • ExecutionContext:执行上下文,封装请求元数据(HTTP/RPC/WebSocket 等)
  • CallHandler:处理器调用器,handle() 方法返回 Observable(RxJS 流),代表处理器的执行结果。
ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
export class TransformInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    // 前置逻辑(请求到达处理器前执行)
    console.log('请求到达处理器前触发');

    // 执行处理器并处理响应流
    return next.handle().pipe(
      map((data) => {
        // 后置逻辑(处理器返回数据后执行)
        return {
          code: 200,
          message: 'success',
          data,
        };
      })
    );
  }
}

参数说明

  1. ExecutionContext(执行上下文)
shell
# 封装了当前请求的上下文信息,兼容 HTTP、RPC(微服务)、WebSocket 等场景,核心方法如下
方法             作用
getArgs()     获取处理器的参数列表(如 HTTP 场景的 req、res)
getHandler() 获取当前执行的处理器方法(Controller 中的方法)
getClass()     获取当前处理器所属的控制器类
switchToHttp() 切换到 HTTP 上下文,返回 HttpArgumentsHost
switchToRpc() 切换到 RPC 上下文(微服务)
switchToWs() 切换到 WebSocket 上下文

# 示例
intercept(context: ExecutionContext, next: CallHandler) {
  const httpContext = context.switchToHttp();
  const request = httpContext.getRequest(); # 获取 Express/Fastify 请求对象
  const response = httpContext.getResponse(); # 获取响应对象
  console.log('请求路径:', request.url);
  return next.handle();
}
  1. CallHandler(调用处理器)
shell
handle():返回 Observable,代表处理器的执行结果(核心);
若不调用 next.handle(),则处理器不会执行(可用于权限拦截、短路逻辑)。
  1. RxJS 操作符
shell
# 拦截器的响应处理依赖 RxJS 操作符
map:转换响应数据;
tap:执行副作用(如日志、埋点,不修改数据);
catchError:捕获并处理异常;
delay:延迟响应;
retry:重试处理器执行。

多个拦截器的执行顺序

当绑定多个拦截器时,执行顺序遵循「前置逻辑正序执行,后置逻辑倒序执行」(类似洋葱模型)

ts
// 绑定顺序:InterceptorA → InterceptorB
@UseInterceptors(InterceptorA, InterceptorB)
findAll() {}

// 执行流程:
// 1. InterceptorA 前置逻辑
// 2. InterceptorB 前置逻辑
// 3. 处理器执行
// 4. InterceptorB 后置逻辑
// 5. InterceptorA 后置逻辑

异步拦截器

拦截器支持异步操作(如从数据库获取配置),只需返回 Observable 或 Promise

ts
@Injectable()
export class AsyncInterceptor implements NestInterceptor {
  constructor(private readonly configService: ConfigService) {}

  async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {
    // 异步获取配置
    const config = await this.configService.get('app');
    console.log('异步配置:', config);

    return next.handle().pipe(map((data) => ({ ...data, config })));
  }
}

跳过拦截器

通过 ExecutionContext 动态跳过拦截器

ts
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
  const handler = context.getHandler();
  // 检查处理器是否有 @SkipInterceptor 装饰器
  if (Reflect.getMetadata('skipInterceptor', handler)) {
    return next.handle(); // 跳过当前拦截器
  }
  // 正常处理
  return next.handle().pipe(map(data => ({ code: 200, data })));
}

// 自定义装饰器
export const SkipInterceptor = () => {
  return (target: any, key: string) => {
    Reflect.defineMetadata('skipInterceptor', true, target, key);
  };
};

// 使用
@Get('raw')
@SkipInterceptor() // 跳过拦截器
getRawData() {
  return { id: 1, name: '未格式化数据' };
}