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

如何让DI容器在Next.js14 App Router服务端组件与服务端动作中可用

在Next.js中为服务端组件和服务端动作共享Awilix DI容器

要让服务端组件(Server Components)和服务端动作(Server Actions)都能访问请求作用域的Awilix容器,可通过**Next.js请求存储(Request Storage)**结合Express中间件实现,具体方案如下:

1. 启用请求存储

在next.config.js中开启Next.js的实验性请求存储功能:

const nextConfig = {
  experimental: {
    requestStorage: true,
  },
};

module.exports = nextConfig;

2. 改造自定义server.ts

在Express中间件中,将Awilix的请求作用域容器存入请求存储:

import express from 'express';
import next from 'next';
import { createContainer } from 'awilix';
import { scopePerRequest, getScope } from 'awilix-express';
import { configureVehicleProfileManagementContainer } from './features/vehicleProfileManagement/diContainerConfig';
import { requestStorage } from 'next/dist/client/components/request-storage';

const dev = process.env.NODE_ENV !== 'production';
const hostname = 'localhost';
const port = 3000;

const app = next({ dev, hostname, port });
const handle = app.getRequestHandler();

// 初始化根容器
const container = createContainer({ strict: true });
configureVehicleProfileManagementContainer(container);

const server = express();
// 启用请求作用域容器
server.use(scopePerRequest(container));

// 将请求作用域容器存入Next.js请求存储
server.use((req, res, next) => {
  const scope = getScope(req);
  if (scope) {
    requestStorage.set('di-container', scope);
  }
  next();
});

app.prepare().then(() => {
  server.get('*', (req, res) => handle(req, res));
  
  server.listen(port, (err?: Error) => {
    if (err) throw err;
    console.log(`> Ready on http://${hostname}:${port}`);
  });
});

3. 封装容器获取工具

新建utils/di-utils.ts,统一容器获取逻辑:

import { requestStorage } from 'next/dist/client/components/request-storage';
import { AwilixContainer } from 'awilix';

// 通用获取容器方法(服务端组件、服务端动作均可调用)
export function getDiContainer(): AwilixContainer {
  const container = requestStorage.get('di-container');
  if (!container) throw new Error('DI容器未初始化,请检查中间件配置');
  return container;
}

4. 在服务端组件中使用

// app/vehicles/page.tsx
import { getDiContainer } from '@/utils/di-utils';

export default async function VehiclePage() {
  const container = getDiContainer();
  const vehicleService = container.resolve('vehicleService');
  const vehicles = await vehicleService.getAllVehicles();

  return (
    <div>
      <h1>车辆列表</h1>
      <ul>{vehicles.map(v => <li key={v.id}>{v.name}</li>)}</ul>
    </div>
  );
}

5. 在服务端动作中使用

// app/actions/vehicleActions.ts
'use server';

import { getDiContainer } from '@/utils/di-utils';

export async function createVehicle(vehicleData: { name: string }) {
  const container = getDiContainer();
  const vehicleService = container.resolve('vehicleService');
  await vehicleService.createVehicle(vehicleData);
}

稳定替代方案(无需实验性API)

如果不想依赖实验性功能,可采用请求ID映射:

  1. 在Express中间件生成唯一请求ID,存入请求头或Cookie
  2. 创建全局Map<string, AwilixContainer>,将请求ID与容器关联
  3. 服务端组件通过cookies()获取请求ID,从Map中取容器;服务端动作从request对象直接取容器或请求ID
  4. 需在请求结束后清理Map中的过期条目,避免内存泄漏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 09:00:17