React在Heroku中向Node.js服务器Fetch数据无请求数据问题排查
Heroku上React向Node.js服务器POST请求无数据的问题解决
问题场景
我在Heroku部署了React前端和Node.js后端,前端通过POST请求向服务器发送数据,但后端打印req时看不到任何请求相关数据。怀疑是Express配置出了问题(为适配Heroku加了express.static(),之前用的express.json()),另外React项目的package.json里proxy还是设置成http://localhost:7000。
前端代码
const { name } = e.target.elements; let details = { name: name.value, }; let response = await fetch("/post", { method: "POST", headers: { "Content-Type": "application/json;charset=utf-8", }, body: JSON.stringify(details), });
服务器端代码
if(process.env.NODE_ENV === "production") { app.use(express.static('../client/build')); app.get('*', (req, res) => { req.sendFile(path.resolve('client/build/index.html', { root: "../" })); }); } app.post("/post", async (req, res) => { const queryRes = await searchFromQuery(req.body.name); res.json({ status: queryRes.isFound ? "found" : "notFound", teaminfo: { name: queryRes.name, number: queryRes.number, avgScore: queryRes.avgScore, predictedScore: queryRes.predictedScore, } }); }); app.listen(PORT, console.log(`Server started on port ${PORT}`)); app.get("/", function(req, res) { res.sendFile('client/build/index.html', { root: "../" }); });
问题原因及解决方案
1. 缺失JSON请求体解析中间件
你的服务器代码里没配置express.json()中间件,导致Express无法解析POST请求的JSON格式请求体,req.body自然是undefined。不管是开发还是生产环境,这个中间件都必须加,而且要放在所有路由定义之前:
// 放在所有路由配置最前面 app.use(express.json());
2. 路由顺序错误
你把生产环境的app.get('*')放在了API路由前面,这会导致所有请求(包括/post接口)都被重定向到index.html,API根本不会被触发。正确的顺序应该是:
- 先配置中间件
- 再定义所有API路由
- 最后配置生产环境的静态文件托管和兜底路由
3. Heroku环境下的Proxy无效
React项目里的proxy字段只在本地开发环境(npm start)生效,生产环境部署到Heroku后完全没用——因为此时React静态文件是由Express托管的,请求直接走Express,不存在跨域代理的需求。
修正后的服务器代码
const express = require('express'); const path = require('path'); const app = express(); // 1. 先配置JSON解析中间件 app.use(express.json()); // 2. 定义API路由 app.post("/post", async (req, res) => { const queryRes = await searchFromQuery(req.body.name); res.json({ status: queryRes.isFound ? "found" : "notFound", teaminfo: { name: queryRes.name, number: queryRes.number, avgScore: queryRes.avgScore, predictedScore: queryRes.predictedScore, } }); }); // 3. 生产环境静态文件托管和兜底路由 if(process.env.NODE_ENV === "production") { app.use(express.static(path.resolve(__dirname, '../client/build'))); // 所有非API请求返回index.html app.get('*', (req, res) => { res.sendFile(path.resolve(__dirname, '../client/build/index.html')); }); } // 原来的app.get("/")可以删掉,*路由已经覆盖了这个场景 const PORT = process.env.PORT || 7000; app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
4. 端口注意事项
Heroku会动态分配端口,所以服务器必须用process.env.PORT,你的代码已经做到了这点。前端请求时直接用相对路径/post就行,不用指定端口,Heroku会自动处理端口映射。
内容的提问来源于stack exchange,提问作者zflint
相关产品推荐
相关产品推荐

