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

React对接Node.js Express时Fetch POST请求失败求助

React + Express 对接问题排查与解决方案

问题概述

React项目中包含Express编写的server.js服务端模块,执行npm run server可正常启动服务,但前端发送fetch POST请求时出现failed to fetch错误,需排查问题并实现正确对接。

核心问题分析

1. Express中间件与路由顺序错误

当前server.js中,app.get('*')通配路由被放在最前面,会拦截所有请求,后续的cors、express.json()解析中间件以及自定义路由都无法生效。同时中间件加载顺序不符合Express规范:解析请求体、CORS等基础中间件必须在路由之前加载。

2. Fetch请求缺少必要请求头

前端fetch发送JSON格式数据时,未设置Content-Type: application/json请求头,导致Express的express.json()中间件无法正确解析req.body,最终获取到的req.body为undefined,引发后续逻辑错误。

3. 路由处理中重复发送响应

/send路由的处理函数中,在transporter.sendMail的回调里已经通过res.json()发送了响应,后续又执行res.status(200).send(content),会触发Cannot set headers after they are sent to the client错误,导致请求异常。

修正后的代码

1. 修正后的server.js

const express = require('express');
const nodemailer = require('nodemailer');
const cors = require('cors');

require('dotenv').config();
const path = require('path');

const app = express();
const port = 8000;
const host = 'localhost';

// 先加载核心中间件:CORS、请求体解析
app.use(cors());
app.use(express.json());

// 自定义路由
const router = express.Router();

router.post('/send', (req, res) => {
  try {
    const { name, number, email, message } = req.body;
    
    // 校验必填参数
    if (!name || !number || !email || !message) {
      return res.status(400).json({ status: 'fail', message: '缺少必填参数' });
    }

    const content = `Name: ${name} \n Phone: ${number} \n Email: ${email} \n Message: ${message} \n`;
    const mail = {
      from: process.env.EMAIL,
      to: 'dimasik11chernyak@gmail.com',
      subject: 'New Message',
      text: content
    };

    transporter.sendMail(mail, (err) => {
      if (err) {
        console.error(err);
        return res.status(500).json({ status: 'fail', message: '邮件发送失败' });
      }
      res.json({ status: 'success', message: '邮件发送成功' });
    });
  } catch (error) {
    console.error(error);
    res.status(400).json({ status: 'fail', message: error.message });
  }
});

app.use('/', router);

// 静态文件服务与SPA路由 fallback(放在所有API路由之后)
app.use(express.static(path.join(__dirname, 'build')));
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

// Nodemailer配置
const transport = {
  service: 'gmail',
  host: 'smtp.gmail.com',
  port: 587,
  auth: {
    user: process.env.EMAIL,
    pass: process.env.PASS
  }
};

const transporter = nodemailer.createTransport(transport);
transporter.verify((error) => {
  if (error) {
    console.error('邮件服务验证失败:', error);
  } else {
    console.log('邮件服务已就绪,可接收消息');
  }
});

const http = require("http");
const server = http.createServer(app);
server.listen(port, host, () => {
  console.log(`服务已启动,监听地址:http://${host}:${port}`);
});

2. 修正后的前端Fetch请求代码

const objectSend = {
  name: inputValues.name,
  email: inputValues.email,
  number: inputValues.number,
  message: inputValues.message
};

try {
  const response = await fetch(`http://localhost:8000/send`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json', // 必须添加该头
    },
    body: JSON.stringify(objectSend)
  });

  if (!response.ok) {
    throw new Error(`请求失败,状态码:${response.status}`);
  }

  const result = await response.json();
  console.log(result); // 处理成功响应
} catch (error) {
  console.error('请求错误:', error); // 捕获并处理错误
}

正确对接流程

生产环境对接

  1. 构建React项目:执行npm run build,生成build目录。
  2. 启动Express服务:执行npm run server,服务会加载build目录下的静态文件,同时处理API请求。
  3. 访问http://localhost:8000即可使用完整应用。

开发环境对接(避免跨域,无需频繁build)

在React项目的package.json中添加代理配置,将API请求转发到Express服务:

"proxy": "http://localhost:8000"

之后前端请求可以简化为:

await fetch('/send', { /* 请求配置 */ });

启动方式:

  1. 先启动Express服务:npm run server。
  2. 再启动React开发服务器:npm start,访问http://localhost:3000即可,API请求会自动转发到8000端口。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 01:17:44