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

如何防止PHP(CodeIgniter)脚本重复执行?

解决CodeIgniter脚本因浏览器/网络问题重复执行导致重复记录的方案

我之前处理过不少类似的重复提交问题,尤其是网络不稳定或者用户误操作(加上浏览器重试机制)引发的重复记录,给你几个在CodeIgniter框架下实用的解决方案,建议多层结合来彻底解决:

1. 前端+后端的一次性令牌防护(核心方案)

CodeIgniter自带CSRF防护,但默认的CSRF令牌是会话周期内有效,没法防同一会话内的重复提交。我们可以扩展成一次性令牌:

  • 后端生成唯一令牌:在渲染表单的控制器方法里,生成一个随机字符串(比如用uniqid()或者UUID),存入Session,同时传给前端表单的隐藏字段
  • 提交验证:接收请求时,先比对前端传来的令牌和Session里的令牌,匹配成功后立刻销毁Session里的令牌,再执行业务逻辑;如果不匹配或者令牌不存在,直接拒绝请求

示例代码:

// 渲染表单的控制器方法
public function create() {
    $token = uniqid('submit_', true);
    $this->session->set_userdata('submit_token', $token);
    $data['submit_token'] = $token;
    $this->load->view('your_form_view', $data);
}

// 处理提交的控制器方法
public function store() {
    $postedToken = $this->input->post('submit_token');
    $sessionToken = $this->session->userdata('submit_token');
    
    if (!$postedToken || $postedToken !== $sessionToken) {
        $this->session->set_flashdata('error', '请勿重复提交');
        redirect('your_form_url');
        return;
    }
    
    // 销毁令牌,防止重复使用
    $this->session->unset_userdata('submit_token');
    
    // 执行你的插入/业务逻辑
    // ...
}

2. 数据库层面添加唯一约束(最后一道防线)

即使前面的防护失效,数据库的唯一约束能直接阻止重复数据插入。根据你的业务场景,找到能唯一标识一条记录的字段组合(比如用户ID+操作类型+日期,或者订单号、业务流水号等),给这个组合添加唯一索引:

ALTER TABLE `your_table` 
ADD UNIQUE KEY `unique_record` (`user_id`, `action_type`, DATE(`created_at`));

这样当重复请求尝试插入相同组合的记录时,数据库会抛出Duplicate Entry错误,你可以在CodeIgniter里捕获这个错误,返回友好提示。

3. 前端禁用提交按钮(防手动重复点击)

虽然没法完全防浏览器的网络重试,但能有效阻止用户因为卡顿多次点击按钮。在表单提交事件里,立刻禁用按钮:

document.getElementById('submit-btn').addEventListener('click', function(e) {
    this.disabled = true;
    // 如果是表单提交,不要阻止默认行为,只是禁用按钮
    document.getElementById('your-form').submit();
});

4. 缓存实现请求去重(针对网络重试)

针对浏览器因为网络超时自动重试的情况,可以用缓存(比如CodeIgniter自带的缓存,或者Redis)记录最近的请求标识,短时间内拒绝重复请求:

  • 前端生成一个唯一请求ID(比如UUID),随表单一起提交
  • 后端先检查缓存里是否存在这个请求ID,如果存在说明是重复请求,直接拒绝;如果不存在,就把请求ID存入缓存,设置过期时间(比如10分钟,覆盖你的重复记录时间差1-2分钟)

示例代码:

$requestId = $this->input->post('request_id');
// 用CodeIgniter的文件缓存,也可以换成Redis缓存
if ($this->cache->get('request_' . $requestId)) {
    echo json_encode(['status' => 'error', 'message' => '请求已处理,请勿重复提交']);
    return;
}

// 存入缓存,10分钟后过期
$this->cache->save('request_' . $requestId, true, 600);

// 执行业务逻辑
// ...

5. 插入前检查最近重复记录(补充方案)

在执行插入操作前,先查询数据库,看看最近N分钟内是否存在相同的业务记录,如果有就跳过插入:

$userId = $this->input->post('user_id');
$businessKey = $this->input->post('business_key'); // 业务上唯一的字段,比如订单号、操作内容等

// 检查最近2分钟内的重复记录
$duplicate = $this->db->where('user_id', $userId)
                      ->where('business_key', $businessKey)
                      ->where('created_at >=', date('Y-m-d H:i:s', strtotime('-2 minutes')))
                      ->get('your_table')
                      ->row();

if ($duplicate) {
    $this->session->set_flashdata('error', '该操作已提交,请勿重复执行');
    redirect('your_form_url');
    return;
}

// 执行插入逻辑
// ...

总结

建议组合使用这些方案:前端禁用按钮 + 后端一次性令牌 + 数据库唯一约束,这样从用户操作、请求验证、数据存储三个层面全方位防护,基本能解决你遇到的重复记录问题。

内容的提问来源于stack exchange,提问作者Akash lal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:18:31