You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NestJS如何通过装饰器实现请求头值自动注入Service方法

实现方案

首先明确核心前提:NestJS 中 Service 默认是全局单例,和单次请求生命周期完全解绑,无法直接凭空获取当前请求的上下文信息,整套实现不需要手动在Controller透传请求头,分三步完成即可。

1. 全局存储请求上下文

用 Node.js 原生的 AsyncLocalStorage 做异步上下文存储即可,不需要额外引入第三方依赖,性能也远高于把Service改成请求作用域的方案。
首先新建上下文工具文件:

// src/context/request-context.ts
import { AsyncLocalStorage } from 'async_hooks';

export interface RequestContext {
  item: string; // 对应请求头 HEADER-ITEM 的值
}

export const requestContextStorage = new AsyncLocalStorage<RequestContext>();

// 工具方法:在任意位置获取当前请求的item值
export function getCurrentRequestItem(): string {
  const store = requestContextStorage.getStore();
  if (!store) throw new Error('不能在请求生命周期外获取请求上下文');
  return store.item;
}

然后写一个全局中间件,在请求进入的第一时间把请求头的值存入上下文:

// src/context/request-context.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { requestContextStorage } from './request-context';

@Injectable()
export class RequestContextMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    const headerItem = req.headers['header-item'] as string;
    // 把请求头值存入异步上下文,后续所有异步调用链都能拿到
    requestContextStorage.run({ item: headerItem }, () => next());
  }
}

在根模块注册全局中间件,让所有路由都生效:

// src/app.module.ts
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { RequestContextMiddleware } from './context/request-context.middleware';

@Module({
  // 保留你原有的 imports、controllers、providers 配置
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(RequestContextMiddleware).forRoutes('*');
  }
}

2. 实现@WithItemFromHeader()装饰器

注意:NestJS 原生的参数装饰器注入逻辑只在Controller层生效,Service层的参数装饰器需要搭配方法装饰器,重写原方法完成参数注入。
新建装饰器文件:

// src/decorators/with-item-from-header.decorator.ts
import { getCurrentRequestItem } from '../context/request-context';

const INJECT_ITEM_META_KEY = Symbol('inject_item_param_index');

// 参数装饰器:标记哪个参数需要注入带item的record对象
export function WithItemFromHeader(): ParameterDecorator {
  return (target, propertyKey, parameterIndex) => {
    // 把需要注入的参数位置存在元数据上
    Reflect.defineMetadata(INJECT_ITEM_META_KEY, parameterIndex, target, propertyKey);
  };
}

// 方法装饰器:重写原方法,自动给标记的参数注入值
export function InjectRequestItem(): MethodDecorator {
  return (target, propertyKey, descriptor: PropertyDescriptor) => {
    const originalMethod = descriptor.value;
    const injectParamIndex = Reflect.getMetadata(INJECT_ITEM_META_KEY, target, propertyKey);

    descriptor.value = async function (...args: any[]) {
      const currentItem = getCurrentRequestItem();
      // 保留原有传入的record字段,追加item值
      args[injectParamIndex] = {
        ...(args[injectParamIndex] || {}),
        item: currentItem
      };
      // 执行业务原逻辑
      return originalMethod.apply(this, args);
    };

    return descriptor;
  };
}

3. 改造业务代码

Controller层不需要再手动提取请求头、透传item参数,直接传业务参数即可:

@Controller()
class UserController {
  @Post("/order")
  @ApiBearerAuth()
  async createOrder(@Body() order: Order) {
    // 直接传业务参数,不需要处理HEADER-ITEM
    return await this.adminService.setOrder(order);
  }
}

Service层按期望的形式使用装饰器即可:

@Injectable()
class AdminService {
  @InjectRequestItem()
  async setOrder(order: Order, @WithItemFromHeader() record: RecordObject) {
    // 这里record已经自动带上item字段,直接使用即可
  }

  @InjectRequestItem()
  async setPurchase(purchase: Purchase, @WithItemFromHeader() record: RecordObject) {
    // 逻辑同上
  }

  @InjectRequestItem()
  async setDate(date: Date, @WithItemFromHeader() record: RecordObject) {
    // 逻辑同上
  }
}

可选简化方案

如果不需要强制用参数注入的形式,甚至可以不用写装饰器,直接在Service方法里调用之前写的getCurrentRequestItem()方法就能拿到请求头的值,代码更精简:

@Injectable()
class AdminService {
  async setOrder(order: Order) {
    const item = getCurrentRequestItem();
    // 直接使用item做业务逻辑
  }
}

注意:不要为了拿请求上下文把Service改成请求作用域(@Injectable({ scope: Scope.REQUEST })),这种方案会导致Service每次请求都重新实例化,不仅性能差,还会引发依赖注入链的作用域溢出问题,AsyncLocalStorage是目前Node.js生态下传递请求上下文的最优方案。

内容的提问来源于stack exchange,提问作者Heartbit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 05:18:53