Next.js+Express自定义服务器POST请求报TypeError问题求助
使用Next.js+自定义Express服务器时POST请求体触发TypeError问题
错误现象
带请求体的POST API请求抛出以下错误:
TypeError: Response body object should not be disturbed or locked
无请求体的请求(如GET、不带体的POST)可正常工作,页面渲染也无异常,但API路由处理失败。
服务器配置代码
import express from 'express'; import dotenv from 'dotenv'; import next from 'next'; import path from 'path'; dotenv.config({ path: path.resolve(__dirname, '../.env'), }); const PORT = Number(process.env.PORT) || 3000; const app = express(); const nextApp = next({ dev: process.env.NODE_ENV !== 'production', port: PORT, hostname: 'http://localhost', }); const nextHandler = nextApp.getRequestHandler(); async function start() { await nextApp.prepare(); app.use(express.json()); // Ensure the body parser is included app.use((req, res) => nextHandler(req, res)); app.listen(PORT, () => { console.log(`Server started, and running at http://localhost:${PORT}`); }); } start();
已尝试的方案
- 引入
express.json()中间件解析JSON请求体 - 调整中间件顺序,确保请求体解析在Next.js处理器之前执行
- 排查确认无其他冲突中间件
版本信息
- Next.js: 14.2.5
- Express: 4.18.2
原因与解决方案
问题原因
Express的express.json()中间件会消费并锁定请求体流,而Next.js的API路由(尤其是App Router下的路由)需要直接访问原始请求体流来处理逻辑。当Express已经解析并锁定请求体后,Next.js再尝试读取时就会触发该错误。
解决步骤
方案1:仅对非Next.js路由应用body解析
如果你的Express服务器有独立的API路由,只在这些路由前使用express.json(),避免干扰Next.js的路由处理:
async function start() { await nextApp.prepare(); // 仅对自定义Express API路由应用body解析 app.use('/api/express', express.json()); // 自定义Express路由示例 app.post('/api/express/test', (req, res) => { res.json({ received: req.body }); }); // 所有其他路由交给Next.js处理 app.all('*', (req, res) => nextHandler(req, res)); app.listen(PORT, () => { console.log(`Server started, and running at http://localhost:${PORT}`); }); }
方案2:禁用Express的body解析,让Next.js自行处理
完全移除express.json()中间件,让Next.js的API路由自己解析请求体。在Next.js的API路由中,通过req.json()方法获取请求体:
// app/api/route.ts (App Router示例) export async function POST(req: Request) { const body = await req.json(); return Response.json({ data: body }); }
同时修改服务器代码,去掉app.use(express.json()):
async function start() { await nextApp.prepare(); // 移除body解析中间件,直接交给Next.js处理 app.all('*', (req, res) => nextHandler(req, res)); app.listen(PORT, () => { console.log(`Server started, and running at http://localhost:${PORT}`); }); }
方案3:重新构建请求对象(兼容性方案)
如果必须保留Express的body解析,可以在传递给Next.js处理器前,重新构建未锁定的请求对象:
app.use(express.json()); app.all('*', (req, res) => { // 重新构建Request对象,避免原始请求体被锁定 const nextReq = new Request(`${req.protocol}://${req.get('host')}${req.originalUrl}`, { method: req.method, headers: req.headers, body: req.body ? JSON.stringify(req.body) : null, credentials: 'include', }); return nextHandler(nextReq, res); });
注意:此方案可能存在兼容性问题,优先推荐前两种方案。
内容的提问来源于stack exchange,提问作者Rayane
相关产品推荐
相关产品推荐

