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

基于Express服务器的Remix应用中异步上下文追踪实现方案

解决Remix + Express中AsyncLocalStorage上下文跨中间件/loader共享问题

核心问题是你原来的实现没有等待异步处理完成就退出了AsyncLocalStorage的上下文,导致Remix的loader/action执行时上下文已经丢失。以下是修正后的完整实现:

1. 修正上下文工具类(ctx.ts)

import { AsyncLocalStorage } from "node:async_hooks";
import { randomUUID } from "node:crypto";

export const asyncLocalStorage = new AsyncLocalStorage<string>();

// 修正:确保await传入的异步函数,避免上下文提前退出
export const createContext = async (fn: () => Promise<void> | void) => {
  // 生成请求唯一的Trace ID
  const traceId = randomUUID();
  await asyncLocalStorage.run(traceId, async () => {
    await fn();
  });
};

export const getTraceId = () => {
  return asyncLocalStorage.getStore();
};

2. 调整Express服务器配置(server.ts)

import { createRequestHandler } from "@remix-run/express";
import express from "express";
import { createContext, getTraceId } from "./ctx";

const initialBuild = await import("./build/index.js");
const app = express();

const remixHandler = createRequestHandler({
  build: initialBuild,
  mode: initialBuild.mode,
});

// 关键:用async中间件包裹createContext,并await它
app.use(async (req, res, next) => {
  await createContext(next);
});

// 测试中间件:可以正常获取Trace ID
app.use((req, res, next) => {
  console.log("中间件中的Trace ID:", getTraceId());
  next();
});

// Remix请求处理:现在loader/action也能拿到上下文
app.all("*", remixHandler);

app.listen(3000, () => console.log("Server running on http://localhost:3000"));

3. 在Remix Loader/Action中验证

import { LoaderFunctionArgs, ActionFunctionArgs } from "@remix-run/node";
import { getTraceId } from "~/ctx";

export const loader = async ({ request }: LoaderFunctionArgs) => {
  console.log("Loader中的Trace ID:", getTraceId());
  return new Response("OK");
};

export const action = async ({ request }: ActionFunctionArgs) => {
  console.log("Action中的Trace ID:", getTraceId());
  return new Response("OK");
};

原理说明

  • 原来的实现中,createContext没有等待next()对应的异步处理完成,导致AsyncLocalStorage的上下文在Remix处理请求前就已销毁。
  • 修正后,通过await fn()确保整个请求的异步处理流程(包括后续中间件、Remix的loader/action执行)都处于AsyncLocalStorage的上下文范围内。
  • 全程共享同一个Trace ID,无需通过getLoadContext传递,实现请求级别的上下文统一。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 17:53:25