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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 03:12:35