跳到主要内容

Request Context 模块

@nest-boot/request-context 模块使用 Node.js AsyncLocalStorage 提供请求作用域存储。它允许你在整个请求生命周期中存储和访问数据,而无需通过每个函数调用传递。

安装

npm install @nest-boot/request-context @nest-boot/middleware
# 或
pnpm add @nest-boot/request-context @nest-boot/middleware

基本用法

模块注册

在应用模块中注册 RequestContextModule

import { Module } from "@nestjs/common";
import { RequestContextModule } from "@nest-boot/request-context";

@Module({
imports: [RequestContextModule],
})
export class AppModule {}

该模块是全局的,只需导入一次。

存储和获取数据

使用静态方法与当前请求上下文交互:

import { Injectable } from "@nestjs/common";
import { RequestContext } from "@nest-boot/request-context";

@Injectable()
export class UserService {
setCurrentUser(user: User) {
RequestContext.set("currentUser", user);
}

getCurrentUser(): User | undefined {
return RequestContext.get<User>("currentUser");
}
}

访问请求 ID

每个上下文都有一个唯一的 ID,可用于日志记录和追踪:

import { Injectable } from "@nestjs/common";
import { RequestContext } from "@nest-boot/request-context";

@Injectable()
export class LoggingService {
log(message: string) {
const requestId = RequestContext.id;
console.log(`[${requestId}] ${message}`);
}
}

访问 HTTP 请求/响应

模块自动存储 Express 的 request 和 response 对象:

import { Injectable } from "@nestjs/common";
import { RequestContext, REQUEST, RESPONSE } from "@nest-boot/request-context";
import { Request, Response } from "express";

@Injectable()
export class HttpContextService {
getRequest(): Request | undefined {
return RequestContext.get<Request>(REQUEST);
}

getResponse(): Response | undefined {
return RequestContext.get<Response>(RESPONSE);
}

getClientIp(): string | undefined {
const req = this.getRequest();
return req?.ip;
}
}

创建自定义上下文

用于后台任务

使用 CreateRequestContext 装饰器为需要独立上下文的方法创建上下文:

import { Injectable } from "@nestjs/common";
import {
CreateRequestContext,
RequestContext,
} from "@nest-boot/request-context";

@Injectable()
export class JobProcessor {
@CreateRequestContext(
(instance, job) => new RequestContext({ type: "job", id: job.id }),
)
async processJob(job: { id: string; data: any }) {
// 此代码在自己的请求上下文中运行
console.log(`Processing job ${RequestContext.id}`);
RequestContext.set("jobData", job.data);
await this.doWork();
}

private async doWork() {
// 上下文在这里仍然可用
const jobData = RequestContext.get("jobData");
}
}

编程式创建上下文

使用 RequestContext.run 手动创建上下文:

import { RequestContext } from "@nest-boot/request-context";

async function processInContext() {
await RequestContext.run(
new RequestContext({ type: "custom", id: "my-id" }),
async (ctx) => {
ctx.set("key", "value");
// 在上下文中执行工作
},
);
}

子上下文

创建继承自父上下文但具有隔离修改的子上下文:

import { RequestContext } from "@nest-boot/request-context";

async function example() {
// 在父上下文中
RequestContext.set("userId", 123);

await RequestContext.child(async (childCtx) => {
// 可以读取父上下文的值
const userId = childCtx.get("userId"); // 123

// 子上下文的修改不影响父上下文
childCtx.set("tempData", "child only");
});

// 父上下文保持不变
RequestContext.get("tempData"); // undefined
}

注册中间件

注册在创建上下文时运行的自定义中间件:

import { RequestContext } from "@nest-boot/request-context";

// 注册中间件
RequestContext.registerMiddleware("auth", async (ctx, next) => {
// 在主回调之前运行
ctx.set("user", await loadUser());
const result = await next();
// 在主回调之后运行
return result;
});

// 带依赖的中间件
RequestContext.registerMiddleware(
"permissions",
async (ctx, next) => {
const user = ctx.get("user");
ctx.set("permissions", await loadPermissions(user));
return next();
},
["auth"], // 在 'auth' 中间件之后运行
);

REPL 支持

启动带有请求上下文支持的 REPL 会话:

// repl.ts
import { repl } from "@nest-boot/request-context";
import { AppModule } from "./app.module";

async function bootstrap() {
await repl(AppModule);
}

bootstrap();

运行:

npx ts-node -r tsconfig-paths/register repl.ts

示例:请求日志

import { Injectable, NestMiddleware } from "@nestjs/common";
import { RequestContext, REQUEST } from "@nest-boot/request-context";
import { Request, Response, NextFunction } from "express";

@Injectable()
export class LoggingMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
const startTime = Date.now();

res.on("finish", () => {
const duration = Date.now() - startTime;
console.log({
requestId: RequestContext.id,
method: req.method,
path: req.path,
status: res.statusCode,
duration: `${duration}ms`,
});
});

next();
}
}

示例:多租户上下文

import { Injectable, NestMiddleware } from "@nestjs/common";
import { RequestContext } from "@nest-boot/request-context";
import { Request, Response, NextFunction } from "express";

@Injectable()
export class TenantMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
const tenantId = req.headers["x-tenant-id"] as string;
RequestContext.set("tenantId", tenantId);
next();
}
}

@Injectable()
export class TenantService {
getCurrentTenant(): string | undefined {
return RequestContext.get<string>("tenantId");
}
}

API 参考

查看完整的 API 文档 获取详细信息。

特性

  • 基于 AsyncLocalStorage - 使用 Node.js AsyncLocalStorage 实现可靠的上下文传播
  • 自动 HTTP 上下文 - 自动为 HTTP 请求创建上下文
  • GraphQL 支持 - 通过拦截器支持 GraphQL 解析器
  • 子上下文 - 创建继承自父上下文的隔离子上下文
  • 中间件系统 - 注册带依赖排序的中间件
  • REPL 支持 - 带上下文支持的交互式调试