Next.js 14中间件中能否运行服务端函数并向请求添加数据?
解决Next.js中间件传递服务端上下文的方案
鉴于中间件无法直接修改请求对象(尤其是GET请求)且不能把敏感信息放请求头,给你几个可行的方案:
方案1:加密后存储到HttpOnly Cookie
- 中间件中通过
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
相关产品推荐
相关产品推荐

