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

Next.js 14中间件中能否运行服务端函数并向请求添加数据?

解决Next.js中间件传递服务端上下文的方案

鉴于中间件无法直接修改请求对象(尤其是GET请求)且不能把敏感信息放请求头,给你几个可行的方案:

  • 中间件中通过getToken拿到会话后,调用你的getContext函数生成上下文
  • 对上下文内容进行对称加密(比如用Node.js内置的crypto模块实现AES算法)
  • 使用NextResponse.cookies.set把加密后的内容存入Cookie,同时设置httpOnly: true、secure: process.env.NODE_ENV === 'production'、sameSite: 'strict',确保前端无法读取该Cookie
  • 在API路由/服务器组件中,读取Cookie里的加密内容,解密后得到完整上下文

示例代码:

// middleware.js
import { getToken } from "next-auth/jwt";
import { NextResponse } from "next/server";
import crypto from "crypto";

// 自己生成32位密钥,存在环境变量中
const ENCRYPTION_KEY = process.env.CONTEXT_ENCRYPTION_KEY;

async function encrypt(text) {
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(ENCRYPTION_KEY), iv);
  let encrypted = cipher.update(JSON.stringify(text));
  encrypted = Buffer.concat([encrypted, cipher.final()]);
  return `${iv.toString('hex')}:${encrypted.toString('hex')}`;
}

export async function middleware(req) {
  const token = await getToken({ req });
  if (token) {
    // 调用你的getContext函数,传入用户ID
    const context = await getContext(token.sub);
    const encryptedContext = await encrypt(context);
    const response = NextResponse.next();
    response.cookies.set({
      name: 'server-context',
      value: encryptedContext,
      httpOnly: true,
      secure: process.env.NODE_ENV === 'production',
      sameSite: 'strict',
      path: '/'
    });
    return response;
  }
  return NextResponse.next();
}
// 服务器端解密工具函数(可放在lib目录下)
import crypto from "crypto";

const ENCRYPTION_KEY = process.env.CONTEXT_ENCRYPTION_KEY;

export async function decrypt(text) {
  const [ivHex, encryptedHex] = text.split(':');
  const iv = Buffer.from(ivHex, 'hex');
  const encryptedText = Buffer.from(encryptedHex, 'hex');
  const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(ENCRYPTION_KEY), iv);
  let decrypted = decipher.update(encryptedText);
  decrypted = Buffer.concat([decrypted, decipher.final()]);
  return JSON.parse(decrypted.toString());
}

// 在API路由中使用
export async function GET(req) {
  const encryptedContext = req.cookies.get('server-context')?.value;
  if (encryptedContext) {
    const context = await decrypt(encryptedContext);
    // 用上下文处理业务逻辑
  }
  // ...
}

方案2:缓存上下文+临时ID传递

  • 中间件生成上下文后,把上下文存入分布式缓存(比如Redis),生成一个唯一临时ID作为缓存键
  • 把这个临时ID存入HttpOnly Cookie
  • 在服务器端读取Cookie中的ID,去缓存中取出上下文,使用后删除缓存(避免无效占用)

这个方案适合上下文内容较大的场景,缺点是需要额外部署缓存服务。

方案3:服务器端统一初始化上下文

  • 放弃在中间件传递上下文,转而在API路由、服务器组件的入口处统一调用getContext
  • 用getServerSession(next-auth)或者getToken获取用户信息,直接生成上下文

示例代码:

// lib/get-server-context.js
import { getServerSession } from "next-auth/next";
import { authOptions } from "../pages/api/auth/[...nextauth]";
import getContext from "./your-getContext-function";

export async function getServerContext(req, res) {
  const session = await getServerSession(req, res, authOptions);
  if (session?.user) {
    return await getContext(session.user.id);
  }
  return null;
}

// 在API路由中使用
import { getServerContext } from "../../lib/get-server-context";

export async function GET(req, res) {
  const context = await getServerContext(req, res);
  // 用上下文处理请求
}

这个方案最直接,不需要依赖中间件,避免了中间件的能力限制,缺点是每个需要上下文的地方都要调用该函数(可以封装成高阶函数或HOC简化重复代码)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 23:52:46