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); // 捕获并处理错误 }
正确对接流程
生产环境对接
- 构建React项目:执行
npm run build,生成build目录。 - 启动Express服务:执行
npm run server,服务会加载build目录下的静态文件,同时处理API请求。 - 访问
http://localhost:8000即可使用完整应用。
开发环境对接(避免跨域,无需频繁build)
在React项目的package.json中添加代理配置,将API请求转发到Express服务:
"proxy": "http://localhost:8000"
之后前端请求可以简化为:
await fetch('/send', { /* 请求配置 */ });
启动方式:
- 先启动Express服务:
npm run server。 - 再启动React开发服务器:
npm start,访问http://localhost:3000即可,API请求会自动转发到8000端口。
内容的提问来源于stack exchange,提问作者Alexandr Boroshenko
相关产品推荐
相关产品推荐

