自托管环境下能否运行Next.js新版middleware及相关报错如何解决
问题解答
1. 自托管环境下Next.js中间件的运行逻辑
Next.js的中间件无论部署在Vercel还是自托管环境都可以正常运行,它不会变成普通express中间件:
- 不管你用
next start自托管Node.js服务还是部署到其他平台,中间件默认都运行在Edge Runtime沙箱环境中,而非普通Node.js上下文 - Edge Runtime有严格的API限制,禁用了
eval、new Function等语法,同时不支持大部分Node.js原生API和依赖这些API的第三方包,这和你部署在哪里无关,只要用Edge Middleware就会受到这些限制
2. eval报错的原因与解决方案
你遇到的eval not allowed报错根本原因是标准jsonwebtoken包依赖了Edge Runtime禁用的语法,且你贴出的代码里包含MongoDB数据库查询逻辑,这类Node.js专属的数据库驱动也无法在Edge Runtime中运行,本质上你的代码场景并不适合使用Edge Middleware。
推荐你采用更适配的方案:放弃使用Edge Middleware,改用Node.js层的中间件封装:
- 你之前使用的
next-connect完全可以继续使用,Next.js没有废弃API路由的Node.js runtime支持,没有强制要求迁移Edge Middleware - 如果不想依赖
next-connect,也可以自行封装高阶函数实现全局认证逻辑,示例如下:
// 封装认证中间件高阶函数 const withAuth = (handler) => async (req, res) => { const { access_token, refresh_token } = req.cookies; if (!access_token) { return handler(req, res); } try { // 你原来的认证逻辑完全可以直接复用,支持jsonwebtoken、MongoDB查询 const { payload, expired } = verifyAccessToken(access_token); if (payload) { if (!payload.userId) { return res.status(401).send("User id is missing in JWT payload."); } const currentUser = await User.findById(payload.userId).lean(); if (!currentUser) { return res.status(401).send("The user belonging to this token no longer exist."); } req.user = currentUser; } const { payload: refreshPayload } = verifyRefreshToken(refresh_token); const user = await User.findOne({ _id: refreshPayload?.userId }); if (refreshPayload && user?.refreshToken === refresh_token && expired) { console.log("Refreshing access token"); const newAccessToken = generateAccessToken(refreshPayload.userId); res.cookie("access_token", newAccessToken, { domain: "localhost", httpOnly: true, secure: process.env.NODE_ENV === "production", path: "/", }); } return handler(req, res); } catch (error) { console.log(error); return handler(req, res); } } // 所有API路由导出时包裹即可 export default withAuth(async function handler(req, res) { // 原API路由逻辑 })
该方案完全运行在普通Node.js上下文,自托管和平台部署都兼容,没有Edge Runtime的各类限制。
3. 你现有Edge Middleware代码的问题
你现有代码中设置Cookie的逻辑是无效的:NextResponse.next().cookie()执行后你没有返回该响应实例,而是重新调用了NextResponse.next()返回了新的空响应,设置的Cookie会直接丢失。如果确实要使用Edge Middleware,需要调整为:
const response = NextResponse.next() response.cookie('access_token', newAccessToken, { // 配置项 }) return response
内容的提问来源于stack exchange,提问作者JonasLevin
相关产品推荐
相关产品推荐

