简单的六种防止数据重复提交的方法!(六招轻松防止数据重复提交!)
原创
一、前端防止重复提交
前端防止数据重复提交核心是通过控制按钮的可用状态,以下是一些常见的方法:
1. 按钮禁用
在提交按钮上添加disabled属性,当点击后立即禁用该按钮,防止二次点击。
<button id="submitBtn" type="submit" disabled>提交</button>
<script>
document.getElementById('submitBtn').addEventListener('click', function() {
this.disabled = true; // 禁用按钮
// 执行提交操作
});
</script>
2. 防止表单重复提交
通过在表单元素上添加一个隐藏的input字段,记录是否已提交,然后在提交前检查该字段。
<form id="myForm">
<input type="hidden" id="isSubmitted" value="0">
<button type="submit">提交</button>
</form>
<script>
document.getElementById('myForm').addEventListener('submit', function() {
if (document.getElementById('isSubmitted').value === '1') {
return false; // 如果已提交,则阻止表单提交
}
document.getElementById('isSubmitted').value = '1'; // 标记为已提交
// 执行提交操作
});
</script>
二、后端防止重复提交
后端防止数据重复提交核心是通过验证请求的唯一性,以下是一些常见的方法:
1. 唯一标识符
为每个请求生成一个唯一标识符,通常使用UUID,然后在提交时将该标识符发送给后端,后端验证该标识符是否已存在。
// 生成UUID的JavaScript代码
function generateUUID() {
var d = new Date().getTime(); // 获取当前时间
if (typeof performance !== 'undefined' && typeof performance.now === 'function'){
d += performance.now(); // 使用高精度时间
}
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (d + Math.random()*16)%16 | 0;
d = Math.floor(d/16);
return (c === 'x' ? r : (r&0x3|0x8)).toString(16);
});
}
// 使用UUID防止重复提交
<button id="submitBtn" type="submit">提交</button>
<script>
var uuid = generateUUID();
document.getElementById('submitBtn').addEventListener('click', function() {
// 在请求中携带UUID
// 执行提交操作,将uuid作为参数发送给后端
});
</script>
2. 令牌机制
为每个用户会话生成一个令牌,提交时将该令牌发送给后端,后端验证令牌是否有效,有效则处理请求,然后立即失效该令牌。
// 假设后端生成了令牌并存储在session中
// 前端获取令牌并发送到后端
<input type="hidden" id="token" value="your-token">
<form id="myForm">
<input type="hidden" name="token" value="<%= session.token %>">
<button type="submit">提交</button>
</form>
// 后端验证令牌
app.post('/submit', function(req, res) {
var token = req.body.token;
if (token === session.token) {
// 处理请求
session.token = null; // 失效令牌
} else {
// 返回谬误信息
}
});
三、数据库防止重复提交
在数据库层面,可以通过以下行为防止数据重复提交:
1. 唯一索引
为也许重复的字段添加唯一索引,如果插入时出现冲突,则捕获异常并处理。
CREATE TABLE your_table (
id INT AUTO_INCREMENT PRIMARY KEY,
unique_field VARCHAR(255) UNIQUE
);
// 插入数据时捕获异常
try {
// 执行插入操作
} catch (Exception $e) {
// 如果捕获到唯一索引冲突的异常,则处理
}
2. 开朗锁
通过在数据表中添加一个版本号字段,每次更新数据时检查版本号是否一致,如果一致则更新数据并增长版本号,否则拒绝更新。
CREATE TABLE your_table (
id INT AUTO_INCREMENT PRIMARY KEY,
version INT,
data VARCHAR(255)
);
// 更新数据时检查版本号
try {
$currentVersion = $database->query("SELECT version FROM your_table WHERE id = $id")->fetchColumn();
$newVersion = $currentVersion + 1;
$database->exec("UPDATE your_table SET data = '$data', version = $newVersion WHERE id = $id AND version = $currentVersion");
} catch (Exception $e) {
// 如果版本号不一致,则处理
}
四、分布式系统防止重复提交
在分布式系统中,可以使用以下方法防止数据重复提交:
1. 分布式锁
使用分布式锁来保证同一时间只有一个请求能执行特定的操作。
// 假设使用Redis实现分布式锁
function acquireLock($key, $value) {
return Redis::set($key, $value, ['nx', 'ex' => 60]);
}
function releaseLock($key, $value) {
$script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
return Redis::eval($script, 1, $key, $value);
}
// 使用分布式锁
try {
if (acquireLock('lock_key', 'unique_value')) {
// 执行操作
releaseLock('lock_key', 'unique_value');
} else {
// 处理锁获取未果的情况
}
} catch (Exception $e) {
// 处理异常
}
2. 消息队列
使用消息队列确保请求按顺序处理,从而避免重复提交。
// 假设使用RabbitMQ作为消息队列
// 生产者发送消息
channel.basic_publish('exchange', 'queue_key', '', json_encode($data));
// 消费者接收消息并处理
channel.basic_consume('callback_function', '', false, false, false, false, function($msg) {
$data = json_decode($msg->body, true);
// 处理数据
$msg->ack();
});
五、其他防止重复提交的方法
除了上述方法,还有一些其他的技巧可以帮助防止数据重复提交:
1. 时间戳
使用时间戳作为请求的一部分,后端验证时间戳是否在合理范围内。
// 前端发送时间戳
var timestamp = Date.now();
// 后端验证时间戳
if (Math.abs(Date.now() - timestamp) < 10000) {
// 时间戳在10秒内,处理请求
} else {
// 时间戳超时,拒绝请求
}
2. 双重检查锁
在代码中实现双重检查锁,以确保在多线程环境下不会重复执行相同的操作。
// Java中的双重检查锁
public synchronized void method() {
if (someCondition) {
synchronized (this) {
if (someCondition) {
// 执行操作
}
}
}
}
六、总结
防止数据重复提交是保证系统稳定性和数据一致性的重要措施。本文介绍了六种常见的方法,包括前端控制、后端验证、数据库约束、分布式系统锁、消息队列以及其他技巧。在实际应用中,可以采取系统的具体情况选择合适的方法或组合使用多种方法,以大致有最佳的保护效果。