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

如何在处理请求的Service中获取所属模块名称?含全局模块场景

NestJS 跨模块获取当前请求所属业务模块名实现方案

核心说明

Execution Context仅提供当前请求链路中正在执行的控制器、路由处理器、入参等运行时信息,没有内置的业务模块元数据映射能力,因此无法直接通过官方API拿到所属业务模块名称,需要结合自定义元数据和异步上下文存储实现。


实现步骤

1. 自定义模块标记装饰器

用于给业务模块/控制器打模块名标记,基于NestJS元数据能力实现:

import { SetMetadata } from '@nestjs/common';

export const MODULE_NAME_KEY = 'module_name';
export const ModuleMeta = (moduleName: string) => SetMetadata(MODULE_NAME_KEY, moduleName);

2. 实现请求上下文存储

用Node.js原生异步本地存储实现跨调用栈的请求数据传递,适配Service层的多层调用场景:

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

export const requestContext = new AsyncLocalStorage<{
  moduleName: string;
  // 可扩展存储其他请求维度数据,如请求ID、用户信息等
}>();

3. 实现全局拦截器注入模块名

在请求入口处提取控制器上标记的模块名,存入异步上下文:

// src/common/module-name.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { MODULE_NAME_KEY } from './module-meta.decorator';
import { requestContext } from './request-context';
import { Observable } from 'rxjs';

@Injectable()
export class ModuleNameInterceptor implements NestInterceptor {
  constructor(private reflector: Reflector) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    // 提取控制器上标记的模块名,也可扩展支持从模块类上提取
    const moduleName = this.reflector.get<string>(MODULE_NAME_KEY, context.getClass()) || 'unknown';
    
    // 绑定上下文传递给后续调用链
    return new Observable(observer => {
      requestContext.run({ moduleName }, () => {
        next.handle().subscribe({
          next: val => observer.next(val),
          error: err => observer.error(err),
          complete: () => observer.complete(),
        });
      });
    });
  }
}

4. 全局注册拦截器

在应用启动时注册全局拦截器,确保所有请求都走模块名注入逻辑:

// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ModuleNameInterceptor } from './common/module-name.interceptor';
import { Reflector } from '@nestjs/core';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalInterceptors(new ModuleNameInterceptor(app.get(Reflector)));
  await app.listen(3000);
}
bootstrap();

5. 业务层使用示例

给业务控制器标记模块名

// src/user/user.controller.ts
import { Controller, Get } from '@nestjs/common';
import { ModuleMeta } from '../common/module-meta.decorator';
import { CommonService } from '../common/common.service';

@Controller('user')
@ModuleMeta('user-module') // 标记当前控制器所属业务模块名
export class UserController {
  constructor(private commonService: CommonService) {}

  @Get()
  getList() {
    // 调用全局通用Service
    return this.commonService.getList();
  }
}

全局通用Service中获取模块名

// src/common/common.service.ts
import { Injectable } from '@nestjs/common';
import { requestContext } from './request-context';

@Injectable()
export class CommonService {
  getList() {
    // 直接从异步上下文获取当前请求所属业务模块名
    const currentModuleName = requestContext.getStore()?.moduleName || 'unknown';
    console.log('调用当前通用服务的业务模块为:', currentModuleName);
    // 后续业务逻辑
    return [];
  }
}

注意事项

  • 该方案默认适配HTTP请求场景,若涉及微服务消息消费、定时任务等非请求场景,需要对应调整上下文注入逻辑
  • 若Service中存在脱离请求上下文的异步调用(如未等待的异步任务、setTimeout回调),需要提前取出模块名手动传递,或绑定异步上下文
  • 若需要给@Module装饰的模块类直接打标记,只需调整拦截器中通过ModuleRef查找控制器所属模块的逻辑即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.12 15:30:00