Axios Post请求后端接收成功但then/catch回调未触发问题排查
问题描述
开发简易React应用时,设置3个输入框分别采集name、lastName、googleId字段,通过onChange事件结合React Hooks管理输入状态,用户点击提交按钮时,会将输入数据发送至Express后端。目前点击按钮后后端控制台可正常打印请求体,输入的字段内容均能被正确接收。按照代码逻辑,.then()回调执行后应当无刷新渲染新增的用户条目,但当前必须手动刷新页面才能看到新提交的用户数据,.then()回调始终未触发导致功能失效,排查数小时尝试多种方案(包括改用fetch实现请求)都无法解决.then()和.catch()均不触发的问题。
React 前端代码
import React from "react"; import Axios from "axios"; function App(){ const [listOfUsers, setListOfUsers] = React.useState([]) const [name, setName] = React.useState(""); const [lastName, setLastName] = React.useState(""); const [googleId, setGoogleId] = React.useState(""); React.useEffect(()=>{ Axios.get("http://localhost:3001/getUsers").then((response)=>{ console.log(response); setListOfUsers(response.data); }); }, []); /*function createUser(){ fetch("http://localhost:3001/createUser", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ name: name, lastName: lastName, googleId: googleId }) }).then(res=>{ console.log("WHY"); console.log(res); }); }*/ console.log("what"); function createUser(){ console.log("what"); /* This prints */ console.log(name); /*This prints the variable name correctly */ const response = Axios.post("http://localhost:3001/createUser", new URLSearchParams({ name: name, lastName: lastName, googleId:googleId }), {headers:{"Content-Type":"application/x-www-form-urlencoded"}} ) .then((response)=>{ console.log("whast"); console.log("why is this not printing"); setListOfUsers(...listOfUsers, {name:name, lastName:lastName, googleId:googleId}) console.log(response); }) /* Nothing in the then response */ .catch((error)=>{ console.log(error.resonse); /*Nothing here either */ }); console.log("hey im done") /* This prints */ } return ( <div className="usersDisplay"> { listOfUsers.map((user)=>{ return (<h1 key={Math.random()}>Name:{user.name}</h1>) }) } <div> <input type="text" name="name" onChange={(event)=>{setName(event.target.value)}} placeholder="Name..." value={name}/> <input type="text" name="lName" onChange={(event)=>{setLastName(event.target.value)}} placeholder="LastName..." value={lastName}/> <input type="text" name="id" onChange={(event)=>{setGoogleId(event.target.value)}} placeholder="GoogleId..." value={googleId}/> <button onClick={createUser}>Create User</button> </div> </div> ) }; export { App };
Express 后端代码
const express = require("express"); const bodyParser = require("body-parser"); const mongoose = require("mongoose"); const cors = require("cors"); const app = express(); app.use(bodyParser.urlencoded({extended:true})); app.use(cors()); /*mongoose.connect("mongodb+srv://{hiddenlogin}I@cluster0.87hm5lv.mongodb.net/?testretryWrites=true&w=majority")*/ mongoose.connect("mongodb+srv://{hiddenlogin}@cluster0.87hm5lv.mongodb.net/?retryWrites=true&w=majority") const userSchema = new mongoose.Schema({ name: String, lastName: String, googleId: String, blogs: [], likedPosts: [] }); const blogSchema = new mongoose.Schema({ name: String, googleId: String, likes: Number, authorFirst: String, authorLast: String, content: String, cutDownContent: String }) const Blog = new mongoose.model("Blog", blogSchema); const User = new mongoose.model("User", userSchema); app.get("/getUsers", (req, res)=>{ User.find({}, (err, results)=>{ if (err){res.json(err)}; res.json(results); }); }); app.post("/createUser", async (req, res)=>{ console.log(req.body); const newUser = new User({ name: req.body.name, lastName: req.body.lastName, googleId: req.body.googleId, blogs: [], likedPosts: [] }); await newUser.save(); res.json({"sucess":true}); console.log("Wow!"); }); app.listen(3001, ()=>{ console.log("Listening on port 3001"); });
问题根因
- 后端接口存在重复发送响应的隐患:
/getUsers接口中错误分支执行res.json(err)后没有终止逻辑,会继续执行后续的res.json(results),触发Cannot set headers after they are sent to the client错误,导致服务端连接异常中断,前端Axios请求长期处于pending状态,既不会触发resolve也不会触发reject,直接造成.then、.catch都不执行。 - 前端catch回调存在拼写错误:
error.resonse应为error.response,就算请求出错进入catch分支,也无法正常打印错误日志,干扰排查方向。 - 前端状态更新写法错误:
setListOfUsers(...listOfUsers, 新用户)缺少数组包裹,就算.then正常触发,也无法正确更新用户列表。 - 后端POST接口未做异常捕获,数据库操作出错时会直接导致请求挂起,无任何响应返回。
- 列表渲染使用
Math.random()作为key,会导致React渲染异常。
修复方案
1. 修复后端接口逻辑
- 所有响应发送后添加return终止逻辑,避免重复发送响应头
- 给POST接口添加try/catch异常捕获
- 等待数据库连接成功后再启动服务,避免连接异常导致请求挂起
- 新建用户成功后直接返回完整用户数据,方便前端使用
const express = require("express"); const bodyParser = require("body-parser"); const mongoose = require("mongoose"); const cors = require("cors"); const app = express(); app.use(bodyParser.urlencoded({extended:true})); app.use(cors()); const userSchema = new mongoose.Schema({ name: String, lastName: String, googleId: String, blogs: [], likedPosts: [] }); const blogSchema = new mongoose.Schema({ name: String, googleId: String, likes: Number, authorFirst: String, authorLast: String, content: String, cutDownContent: String }) const Blog = new mongoose.model("Blog", blogSchema); const User = new mongoose.model("User", userSchema); app.get("/getUsers", (req, res)=>{ User.find({}, (err, results)=>{ if (err){ return res.status(500).json(err) } return res.json(results); }); }); app.post("/createUser", async (req, res)=>{ try { console.log(req.body); const newUser = new User({ name: req.body.name, lastName: req.body.lastName, googleId: req.body.googleId, blogs: [], likedPosts: [] }); await newUser.save(); console.log("Wow!"); return res.json({"success":true, data: newUser}); } catch (err) { console.log("创建用户失败", err); return res.status(500).json(err); } }); // 等数据库连接成功再启动服务 mongoose.connect("mongodb+srv://{hiddenlogin}@cluster0.87hm5lv.mongodb.net/?retryWrites=true&w=majority") .then(()=>{ console.log("数据库连接成功"); app.listen(3001, ()=>{ console.log("Listening on port 3001"); }); }) .catch(err=>{ console.log("数据库连接失败", err); })
2. 修复前端逻辑
- 修正catch回调的拼写错误
- 修正列表状态更新的写法,使用函数式更新避免闭包拿到旧状态
- 修正列表渲染的key,优先使用数据库生成的唯一_id
import React from "react"; import Axios from "axios"; function App(){ const [listOfUsers, setListOfUsers] = React.useState([]) const [name, setName] = React.useState(""); const [lastName, setLastName] = React.useState(""); const [googleId, setGoogleId] = React.useState(""); React.useEffect(()=>{ Axios.get("http://localhost:3001/getUsers").then((response)=>{ console.log(response); setListOfUsers(response.data); }).catch(err=>{ console.log("获取用户列表失败", err.response); }); }, []); function createUser(){ Axios.post("http://localhost:3001/createUser", new URLSearchParams({ name: name, lastName: lastName, googleId:googleId }), {headers:{"Content-Type":"application/x-www-form-urlencoded"}} ) .then((response)=>{ console.log("创建用户成功", response); // 函数式更新状态,避免闭包问题 setListOfUsers(prev => [...prev, { name, lastName, googleId, _id: response.data.data._id }]) // 提交成功后清空输入框 setName(""); setLastName(""); setGoogleId(""); }) .catch((error)=>{ // 修正拼写错误 console.log("创建用户失败", error.response); }); } return ( <div className="usersDisplay"> { listOfUsers.map((user)=>{ // 优先用用户_id作为key,避免随机数导致渲染异常 return (<h1 key={user._id}>Name:{user.name} {user.lastName}</h1>) }) } <div> <input type="text" name="name" onChange={(event)=>{setName(event.target.value)}} placeholder="Name..." value={name}/> <input type="text" name="lName" onChange={(event)=>{setLastName(event.target.value)}} placeholder="LastName..." value={lastName}/> <input type="text" name="id" onChange={(event)=>{setGoogleId(event.target.value)}} placeholder="GoogleId..." value={googleId}/> <button onClick={createUser}>Create User</button> </div> </div> ) }; export { App };
修改完成后重启前后端服务,即可正常触发.then回调,提交后无需刷新页面即可看到新增的用户条目。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

