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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 06:21:14