讲师中心 微信公众号
AI工具推荐 视频效率加速

Ajax 表单提交导致数据库重复插入的根因分析与终极解决方案

轻墨大大_1475

轻墨大大_1475

发布时间:2026-04-16 20:04:04

|

351人浏览过

|

来源于php中文网

原创

Ajax 表单提交导致数据库重复插入的根因分析与终极解决方案

本文深入剖析 ajax 调用 php 后端时出现“一次点击、两次插入”的典型问题,揭示隐藏在数据库连接函数中的隐式执行陷阱,并提供涵盖前端防重、后端幂等、数据库约束三层防护的专业级解决方案。

本文深入剖析 ajax 调用 php 后端时出现“一次点击、两次插入”的典型问题,揭示隐藏在数据库连接函数中的隐式执行陷阱,并提供涵盖前端防重、后端幂等、数据库约束三层防护的专业级解决方案。

在您提供的代码中,看似规范的 Ajax 提交流程(点击按钮 → 触发 opnActPag.addAct() → POST 至 save.php)却持续导致数据库重复插入,而返回 ID 却仅有一个——这强烈暗示问题并非来自客户端重复请求,而是服务端逻辑中存在未察觉的隐式执行分支。正如您最终定位到的关键线索:poke_db() 函数中这一行代码是罪魁祸首:

if( !$conn -> query($sql) ){  // ⚠️ 危险!此处已执行了一次 SQL!
    die('[ERR] ' . $conn -> error);
}

该判断语句在检查 SQL 执行结果前,已主动调用 $conn->query($sql) 并完成了一次真实插入。随后在下方逻辑中($result = mysqli_query($conn, $sql);),同一 SQL 再次被执行——形成稳定复现的“双写”。这是典型的副作用误用(side-effect misuse):将“执行”与“判断执行结果”错误地耦合在同一表达式中。

✅ 正确修复:分离执行与校验逻辑

修改 poke_db() 函数,确保 SQL 仅执行一次,且执行逻辑集中可控:

function poke_db($sql, $multi = 0, $array = 0) {
    global $db_name, $srv, $usr, $pas;

    $conn = mysqli_init();
    $conn->options(MYSQLI_OPT_CONNECT_TIMEOUT, 5);
    $conn->real_connect($srv, $usr, $pas, $db_name);
    if ($conn->connect_error) {
        die('[ERR] Connection: ' . $conn->connect_error);
    }

    // ✅ 关键修正:只在此处执行一次 SQL,不再提前 query()
    if ($multi == 1) {
        $result = mysqli_multi_query($conn, $sql);
    } else {
        $result = mysqli_query($conn, $sql); // ← 唯一执行点
        $id = $result ? mysqli_insert_id($conn) : null;
    }

    if (!$result) {
        die('[ERR] Query failed: ' . $conn->error . ' | SQL: ' . htmlspecialchars($sql));
    }

    if ($array == 1) {
        $out_array = [];
        if (mysqli_num_rows($result) > 0) {
            while ($row = mysqli_fetch_assoc($result)) {
                $out_array[] = $row;
            }
        }
        return $out_array;
    } else {
        return $id ?? 0;
    }
}

? 验证建议:在 save.php 中添加日志,确认 poke_db() 调用前后是否仅有一条 INSERT 日志:

error_log("[SAVE.PHP] Insert attempt for user {$_POST['usr']}, id {$_POST['id']} — " . date('Y-m-d H:i:s'));

?️ 防御升级:构建三层幂等保障体系

仅修复执行逻辑仍不够稳健。真实生产环境需叠加以下防护,杜绝任何漏网之鱼:

jQuery金融借款单提交表单
jQuery金融借款单提交表单

jQuery金融借款单提交表单,包含出借金额和日期的选择,且自动计算利息,非常的简单方便。

下载

1. 前端层:禁用按钮 + 防抖 + Token 校验

opnActPag['addAct'] = function () {
    const btn = document.getElementById('act_subm_txt');
    btn.disabled = true; // 立即禁用,防止连点
    btn.textContent = '提交中...';

    // 添加一次性 CSRF Token(服务端生成并注入到页面)
    const token = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';

    $.ajax({
        type: 'POST',
        url: 'save.php',
        data: { 
            act_txt: actTxtArea.value,
            id: actHidVal,
            usr: actHidUsr,
            _token: token // 传入 Token
        },
        success: function(data) {
            console.log('Success:', data);
        },
        error: function() {
            alert('提交失败,请重试');
        },
        complete: function() {
            btn.disabled = false;
            btn.textContent = '✓';
        }
    });
};

2. 后端层:强制幂等 + 事务原子性

save.php 应使用预处理语句(杜绝 SQL 注入)、显式事务及唯一约束校验:

<?php
session_start();
// ✅ 验证 CSRF Token
if (!hash_equals($_SESSION['csrf_token'] ?? '', $_POST['_token'] ?? '')) {
    http_response_code(403);
    exit('Invalid token');
}

if (isset($_POST['act_txt']) && !empty(trim($_POST['act_txt']))) {
    $act_txt = trim($_POST['act_txt']);
    $usr = filter_var($_POST['usr'], FILTER_SANITIZE_STRING);
    $id = (int)$_POST['id'];

    // ✅ 使用 PDO 预处理 + 事务(更安全可靠)
    try {
        $pdo = new PDO("mysql:host=localhost;dbname=dbname", "root", "pass");
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

        $pdo->beginTransaction();

        // ✅ 先查后插(或直接 ON DUPLICATE KEY UPDATE)
        $stmt = $pdo->prepare(
            "INSERT INTO actions (user_name, info, level_3_id, created_at) 
             VALUES (?, ?, ?, NOW()) 
             ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id)"
        );
        $stmt->execute([$usr, preg_replace('/[\r\n]+/', ' ', htmlentities($act_txt, ENT_QUOTES)), $id]);

        $insertId = $pdo->lastInsertId();
        $pdo->commit();

        echo json_encode(['success' => true, 'id' => $insertId]);

    } catch (PDOException $e) {
        $pdo->rollback();
        error_log("DB Error: " . $e->getMessage());
        echo json_encode(['success' => false, 'error' => 'Insert failed']);
    }
}
?>

3. 数据库层:强制唯一索引(终极兜底)

在 actions 表上添加复合唯一索引,确保 (user_name, info, level_3_id) 组合不可重复:

ALTER TABLE actions 
ADD CONSTRAINT uk_user_info_level UNIQUE (user_name, info, level_3_id);

当重复插入发生时,数据库将直接抛出 1062 Duplicate entry 错误,由后端捕获并优雅降级(如返回“数据已存在”),而非静默写入脏数据。

⚠️ 重要注意事项

  • 永远不要拼接 SQL 字符串:原代码中 '.$_POST['usr'].' 极易引发 SQL 注入,必须使用预处理语句。
  • 避免 htmlentities() 在入库前双重编码:应仅在输出 HTML 时编码,入库应存原始内容(或统一 UTF-8 存储)。
  • mysqli_insert_id() 依赖于上一次成功 INSERT:若前序查询非 INSERT(如 SELECT),该函数可能返回 0 或错误值,务必结合 $result 判断。
  • PHP 8.1+ 更严格的行为:如您所遇,PHP 8.1 对错误抑制和执行时序更敏感,旧有“侥幸通过”的逻辑会迅速暴露,升级后务必全面审查副作用代码。

通过本次修复,您不仅解决了眼前问题,更建立了一套可复用于订单、用户注册、日志记录等所有关键写操作的工业级幂等框架——这才是专业后端开发的核心能力。

热门AI工具

更多
WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

Atoms
Atoms Hot

Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

AionClaw
AionClaw Hot

AionClaw是一款面向办公、创作和编程任务的AI桌面智能体。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

相关专题

更多
ajax教程
ajax教程

php中文网为大家带来ajax教程合集,Ajax是一种用于创建快速动态网页的技术。通过在后台与服务器进行少量数据交换,Ajax可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。php中文网还为大家带来ajax的相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

3100

2023.06.14

ajax中文乱码解决方法
ajax中文乱码解决方法

ajax中文乱码解决方法有设置请求头部的字符编码、在服务器端设置响应头部的字符编码和使用encodeURIComponent对中文进行编码。本专题为大家提供ajax中文乱码相关的文章、下载、课程内容,供大家免费下载体验。

360

2023.08.31

ajax传递中文乱码怎么办
ajax传递中文乱码怎么办

ajax传递中文乱码的解决办法:1、设置统一的编码方式;2、服务器端编码;3、客户端解码;4、设置HTTP响应头;5、使用JSON格式。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2913

2023.11.15

ajax网站有哪些
ajax网站有哪些

使用ajax的网站有谷歌、维基百科、脸书、纽约时报、亚马逊、stackoverflow、twitter、hacker news、shopify和basecamp等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1161

2024.09.24

ThinkPHP表单验证与数据安全处理实战
ThinkPHP表单验证与数据安全处理实战

本专题聚焦 ThinkPHP 在表单处理中的验证与安全机制,系统讲解验证器使用、自定义规则、场景验证以及错误提示处理。内容涵盖 XSS 防护、SQL 注入防御、数据过滤与输入校验等关键安全措施。通过实际案例,帮助开发者构建安全可靠的数据处理流程。

261

2026.03.30

Gradle插件开发入门教程
Gradle插件开发入门教程

本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

120

2026.10.22

Gradle Wrapper配置教程
Gradle Wrapper配置教程

本专题介绍Gradle Wrapper的基本作用,涵盖文件组成、生成方法、版本配置和任务调用,帮助你理解项目如何使用指定的Gradle版本,减少不同开发环境带来的构建差异。

60

2026.10.22

Golangci-lint检查器使用教程
Golangci-lint检查器使用教程

本专题整理Golangci-lint常用Linters的用途与配置方法,覆盖错误处理、无效代码、安全、复杂度、命名和依赖检查,帮助项目合理选规则。

60

2026.10.22

Gradle怎么构建Java项目
Gradle怎么构建Java项目

本专题介绍Gradle Java项目构建方法,涵盖Java插件配置、依赖声明、源码编译、测试执行和打包检查,帮助你理解各阶段的任务关系,逐步完成项目构建。

40

2026.10.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
jQuery官方API文档
jQuery官方API文档

共0课时 | 0人学习

Symfony教程(入门篇+基础篇)
Symfony教程(入门篇+基础篇)

共18课时 | 1.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn