6个超实用方法,轻松有效防止表单重复提交
AI 概述
前端防止重复提交1. 按钮禁用2. 防抖函数3. 请求拦截后端防止重复提交4. Token 令牌机制5. AOP + Redis 方案6. 数据库唯一约束方案对比实践建议
重复提交是开发中经常遇到的问题。用户可能因为页面响应慢连续点击提交按钮,或者网络延迟时反复重试。这些情况会导致数据重复、业务混乱,比如生成重复订...
目录
重复提交是开发中经常遇到的问题。用户可能因为页面响应慢连续点击提交按钮,或者网络延迟时反复重试。这些情况会导致数据重复、业务混乱,比如生成重复订单、多次扣款等问题。
防止重复提交需要前后端配合。前端主要提升用户体验,后端才是真正的保障。

前端防止重复提交
前端方法能防止用户误操作,但不能完全依赖,因为可以通过工具绕过前端验证。
1. 按钮禁用
提交后立即禁用按钮,这是最直接的方法。
functionsubmitForm() {
const btn = document.getElementById('submitBtn');
// 如果按钮已禁用,直接返回
if (btn.disabled) return;
// 禁用按钮并改变文字
btn.disabled = true;
btn.textContent = '提交中...';
// 发送请求
fetch('/api/submit', {
method: 'POST',
body: JSON.stringify(formData)
})
.then(response => response.json())
.then(data => {
// 处理响应
})
.catch(error => {
console.error('Error:', error);
})
.finally(() => {
// 无论成功失败,都重新启用按钮
btn.disabled = false;
btn.textContent = '提交';
});
}
2. 防抖函数
控制按钮在指定时间内只能点击一次。
functiondebounce(func, wait) {
let timeout;
returnfunction() {
const context = this;
const args = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(context, args);
}, wait);
};
}
// 使用示例
const submitForm = debounce(function() {
// 实际的提交逻辑
}, 1000); // 1 秒内只能点击一次
3. 请求拦截
记录正在处理的请求,发现重复请求直接拦截。
classRequestManager{
constructor() {
this.pendingRequests = newMap();
}
generateKey(config) {
return`${config.method}-${config.url}-${JSON.stringify(config.data)}`;
}
addRequest(config) {
const key = this.generateKey(config);
if (this.pendingRequests.has(key)) {
returnfalse;
}
this.pendingRequests.set(key, true);
returntrue;
}
removeRequest(config) {
const key = this.generateKey(config);
this.pendingRequests.delete(key);
}
}
// 在 axios 拦截器中使用
const requestManager = new RequestManager();
axios.interceptors.request.use(config => {
if (!requestManager.addRequest(config)) {
returnPromise.reject(newError('请求已处理中'));
}
return config;
});
axios.interceptors.response.use(response => {
requestManager.removeRequest(response.config);
return response;
}, error => {
if (error.config) {
requestManager.removeRequest(error.config);
}
returnPromise.reject(error);
});
前端方法的优点是提升用户体验,缺点是可以被绕过。因此后端验证是必须的。
后端防止重复提交
4. Token 令牌机制
这是比较传统但有效的方法,适合表单提交场景。工作流程:
- 用户访问页面时,后端生成唯一 Token;
- Token 随页面返回给前端;
- 提交表单时携带 Token;
- 后端验证 Token 有效性;
- 验证成功后立即删除 Token。
Java 实现示例:
@Component
publicclassTokenService{
// 生成 Token
publicString createToken(HttpServletRequest request) {
String token = UUID.randomUUID().toString();
// 存储到 Session 中
request.getSession().setAttribute("FORM_TOKEN", token);
return token;
}
// 验证 Token
publicboolean verifyToken(HttpServletRequest request) {
String clientToken = request.getParameter("token");
if (clientToken == null) {
returnfalse;
}
HttpSession session = request.getSession();
String serverToken = (String) session.getAttribute("FORM_TOKEN");
if (serverToken == null || !serverToken.equals(clientToken)) {
returnfalse;
}
// 验证成功后立即删除
session.removeAttribute("FORM_TOKEN");
returntrue;
}
}
前端表单:
<formaction="/submit"method="post">
<inputtype="hidden"name="token"value="${token}">
<!-- 其他表单字段 -->
<buttontype="submit">提交</button>
</form>
5. AOP + Redis 方案
适合分布式系统,利用 Redis 实现分布式锁。定义注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public@interface PreventDuplicate {
intexpire()default 5; // 锁定时间,默认 5 秒
String key()default ""; // 自定义锁 key
}
实现切面:
@Aspect
@Component
publicclassDuplicateSubmitAspect{
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Around("@annotation(preventDuplicate)")
publicObject checkDuplicate(ProceedingJoinPoint joinPoint,
PreventDuplicate preventDuplicate) throws Throwable {
HttpServletRequest request = getRequest();
String lockKey = buildLockKey(request, preventDuplicate);
int expireTime = preventDuplicate.expire();
// 尝试加锁
Boolean success = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", Duration.ofSeconds(expireTime));
if (!success) {
thrownewRuntimeException("请勿重复提交");
}
try {
return joinPoint.proceed(); // 执行原方法
} finally {
// 根据业务需求决定是否立即删除锁
// redisTemplate.delete(lockKey);
}
}
privateString buildLockKey(HttpServletRequest request,
PreventDuplicate preventDuplicate) {
String userId = getUserId(request); // 获取用户 ID
String uri = request.getRequestURI();
String params = request.getQueryString() != null ?
request.getQueryString() : "";
return"submit:lock:" + userId + ":" + uri + ":" +
DigestUtils.md5DigestAsHex(params.getBytes());
}
private HttpServletRequest getRequest() {
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) attributes;
return sra.getRequest();
}
}
使用方式:
@PostMapping("/order/create")
@PreventDuplicate(expire = 10)
public Result createOrder(@RequestBody OrderDTO order) {
// 业务逻辑
returnResult.success("订单创建成功");
}
6. 数据库唯一约束
对于有唯一性要求的业务,可以在数据库层面保障。例如订单表:
CREATETABLE orders ( idBIGINT PRIMARY KEY, order_no VARCHAR(64) UNIQUE, -- 订单号唯一约束 user_id BIGINT, amount DECIMAL(10,2), create_time DATETIME );
在业务代码中处理:
@Service
publicclassOrderService{
public Result createOrder(OrderDTO order) {
try {
// 尝试插入订单
orderMapper.insert(order);
return Result.success("创建成功");
} catch (DuplicateKeyException e) {
// 捕获唯一约束异常
log.warn("重复订单: {}", order.getOrderNo());
return Result.error("订单已存在");
}
}
}
方案对比
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 按钮禁用 | 所有前端表单 | 用户体验好 | 可被绕过 |
| Token 机制 | 表单提交 | 安全可靠 | 分布式环境需要共享 Session |
| AOP+Redis> | 分布式系统 | 无侵入,灵活 | 依赖 Redis |
| 数据库约束 | 有唯一性要求 | 绝对可靠 | 只能防止最终重复 |
实践建议
- 前后端结合使用:前端防止误操作,后端保障数据安全;
- 合理设置超时时间:一般 5-10 秒足够,避免影响正常操作;
- 友好提示用户:不要直接报错,提示”操作进行中”或”请勿重复提交”;
- 记录重复提交:监控重复提交情况,帮助优化系统;
- 考虑幂等性:重要业务要实现幂等接口。
防止重复提交是系统稳定性的基础保障。选择方案时要根据实际业务需求,有时候需要多种方案组合使用,才能达到最好的效果。
最重要的是,不要完全依赖前端的防护,后端必须要有相应的验证机制。这样才能确保系统的数据安全和业务稳定。
其他推荐:
《JavaScript 防止表单重复提交,指定时间内禁止重复点击》
以上关于6个超实用方法,轻松有效防止表单重复提交的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。
声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » 6个超实用方法,轻松有效防止表单重复提交
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » 6个超实用方法,轻松有效防止表单重复提交

微信
支付宝