Express.js+React.js实现服务端错误提示前端弹窗展示
实现方案
问题核心原因
- 后端检测到FirstName重复时,仅在服务端控制台打印日志,没有向前端返回标准的HTTP错误响应,前端无法接收错误消息
- 前端将成功提示、页面跳转逻辑写在了axios请求外部,无论接口请求成功或失败都会执行,即使用户名重复也会弹出成功提示、强制跳转页面
- 后端数据库查询逻辑存在漏洞:MySQL查询无论是否匹配到结果都会返回数组类型,直接判断
if(result)会把空数组判定为真值,重复判断逻辑本身就有问题,同时缺少数据库异常捕获,出错时前端会一直等待响应。
第一步:修改后端POST接口
调整接口逻辑,所有分支都给前端返回明确的响应,重复场景返回400状态码+错误提示:
// 新增记录接口 app.post("/api/post", (req, res) => { const { FirstName, LastName, FatherName, userCNIC, Contact, Gender } = req.body; const sqlGet = "SELECT id FROM emp_table WHERE FirstName = ?"; // 查询时参数统一用数组传递,避免SQL参数格式错误 db.query(sqlGet, [FirstName], (error, result) => { // 捕获数据库查询异常 if (error) { return res.status(500).send("服务异常,请稍后重试"); } // 查询结果是数组,长度大于0才代表存在重名记录 if (result.length > 0) { // 返回400错误状态,把重名提示返回给前端 return res.status(400).send("用户名已存在"); } // 无重名时执行插入逻辑 const sqlInsert = "INSERT INTO emp_table (FirstName, LastName, FatherName, userCNIC, Contact, Gender) VALUES (?, ?, ?, ?, ?, ?)"; db.query( sqlInsert, [FirstName, LastName, FatherName, userCNIC, Contact, Gender], (insertError, insertResult) => { if (insertError) { return res.status(500).send("数据保存失败,请重试"); } res.send("Values inserted!"); } ); }); });
注意:如果编辑更新用户时也需要校验FirstName不能和其他用户重复,给
/api/update接口加上同样的重复判断逻辑即可,记得排除当前编辑用户自身的记录。
第二步:修改前端提交逻辑
把成功提示、清空表单、页面跳转的逻辑全部挪到axios的then回调中,只有接口返回成功才执行这些操作,错误场景直接触发已有的toast错误提示,不跳转页面:
const handleSubmit = (e) => { e.preventDefault(); const phoneno = /^\d{11,12}$/; const cnicno = /^\d{12,13}$/; // 格式校验不通过直接return,不需要重复执行e.preventDefault if (!Contact.match(phoneno)) { toast.error("请输入正确的手机号!"); return; } if (!userCNIC.match(cnicno)) { toast.error("请输入正确的CNIC编号!"); return; } if (!id) { // 新增用户场景 axios .post("http://localhost:5000/api/post", { FirstName, LastName, FatherName, userCNIC, Contact, Gender, }) .then(() => { // 仅请求成功时执行:弹成功提示、清空表单、跳转首页 toast.success("联系人添加成功"); setState(initialState); setTimeout(() => navigate("/"), 500); }) .catch((err) => { // 请求失败(包括重名错误)时弹对应错误提示,不跳转 toast.error(err.response.data); }); } else { // 编辑更新场景 axios .put(`http://localhost:5000/api/update/${id}`, { FirstName, LastName, FatherName, userCNIC, Contact, Gender, }) .then(() => { toast.success("联系人更新成功"); setState(initialState); setTimeout(() => navigate("/"), 500); }) .catch((err) => { toast.error(err.response.data); }); } };
可选:实现输入框下方内联错误提示
如果不想用弹窗toast,要在FirstName输入框旁显示固定的错误提示div,按以下方式修改即可:
- 新增错误消息状态:
const [errMsg, setErrMsg] = useState(""); - 在FirstName输入块下方加错误提示div,同时输入时自动清空旧错误:
<Form.Group controlId="form.Name"> <Form.Label>Name</Form.Label> <Form.Control type="text" placeholder="Enter name" name="FirstName" onChange={(e) => { handleInputChange(e); setErrMsg(""); // 输入时清空旧的错误提示 }} value={FirstName || ""} id="FirstName" /> {/* 错误提示区域 */} {errMsg && <div className="text-danger mt-1">{errMsg}</div>} </Form.Group> - 把axios catch里的toast.error改成给errMsg赋值即可:
.catch((err) => setErrMsg(err.response.data))
内容的提问来源于stack exchange,提问作者SarfarazAhmed
相关产品推荐
相关产品推荐

