Express应用中如何阻止URL触发的脚本重复执行?
解决Express中URL触发脚本重复执行的问题
问题根源
你用GET请求处理数据插入操作,浏览器刷新页面时会自动重新发送当前页面的请求,导致插入逻辑重复执行。GET语义上应仅用于获取资源,而非修改/创建资源,这是导致重复提交的核心原因。
解决方案
方案1:改用POST请求处理插入(推荐)
遵循REST规范,用POST请求处理数据写入操作,浏览器刷新时会提示用户是否重新提交,避免无意识的重复执行。
修改代码如下:
// 把app.get改为app.post app.post("/api/user/med", (req, res) => { try { // POST请求参数需从req.body获取,需先配置解析中间件 const { name, expy, quan, place } = req.body; if (!name) { return res.status(400).send("name参数不能为空"); } const sql = "INSERT INTO medicine (med_name, expiry, quantity, place) VALUES ($1, $2, $3, $4)"; const values = [name, expy, quan, place]; client.query(sql, values, (err) => { if (err) throw err; // 插入成功后重定向,避免刷新重复提交 res.redirect("/"); }); } catch (err) { res.status(500).send(err.message); } }); // 在路由前配置POST参数解析中间件 app.use(express.urlencoded({ extended: true })); app.use(express.json());
前端发起请求示例(表单方式):
<form action="/api/user/med" method="POST"> <input type="text" name="name" placeholder="药品名称"> <input type="text" name="expy" placeholder="有效期"> <input type="number" name="quan" placeholder="数量"> <input type="text" name="place" placeholder="存放位置"> <button type="submit">提交</button> </form>
方案2:重定向避免重复执行(兼容GET场景)
如果必须保留GET请求,插入成功后不要直接render页面,而是重定向到展示页面。这样刷新时只会请求重定向后的页面,不会触发插入逻辑:
app.get("/api/user/med", (req, res) => { try { const { name, expy, quan, place } = req.query; if (!name) { return res.status(400).send("name参数不能为空"); } const sql = "INSERT INTO medicine (med_name, expiry, quantity, place) VALUES ($1, $2, $3, $4)"; const values = [name, expy, quan, place]; client.query(sql, values, (err) => { if (err) throw err; // 插入成功后重定向到列表页 res.redirect("/medicine-list"); }); } catch (err) { res.status(500).send(err.message); } }); // 新增列表展示路由 app.get("/medicine-list", (req, res) => { client.query("SELECT * FROM medicine", (err, result) => { if (err) throw err; // 注意是result.rows(小写),原代码的result.Rows是错误写法 res.render("index.ejs", { resultRows: result.rows }); }); });
方案3:添加请求唯一标识(幂等处理)
给每个请求生成唯一ID(如UUID),插入时先检查该ID是否已存在于数据库中,不存在才执行插入:
- 给
medicine表新增request_id字段并设置唯一约束 - 实现代码如下:
const { v4: uuidv4 } = require('uuid'); // 需安装uuid包:npm install uuid app.get("/api/user/med", (req, res) => { try { const { name, expy, quan, place, requestId } = req.query; if (!name || !requestId) { return res.status(400).send("参数不完整"); } // 先检查requestId是否已存在 const checkSql = "SELECT id FROM medicine WHERE request_id = $1"; client.query(checkSql, [requestId], (err, checkResult) => { if (err) throw err; if (checkResult.rows.length > 0) { // 已执行过,直接跳转列表页 return res.redirect("/medicine-list"); } // 执行插入 const insertSql = "INSERT INTO medicine (med_name, expiry, quantity, place, request_id) VALUES ($1, $2, $3, $4, $5)"; const values = [name, expy, quan, place, requestId]; client.query(insertSql, values, (err) => { if (err) throw err; res.redirect("/medicine-list"); }); }); } catch (err) { res.status(500).send(err.message); } });
额外注意点
- 原代码中
result.Rows是错误写法,正确应为result.rows(小写) - 修改
req.query或req.url无法阻止重复请求,这些修改仅对当前请求有效,刷新是浏览器发起的全新请求,不受影响
内容的提问来源于stack exchange,提问作者Progammer
相关产品推荐
相关产品推荐

