有效防止重复提交的六个方法

重复提交是开发中经常遇到的问题。用户可能因为页面响应慢连续点击提交按钮,或者网络延迟时反复重试。这些情况会导致数据重复、业务混乱,比如生成重复订单、多次扣款等问题。最重要的是,不要完全依赖前端的防护,后端必须要有相应的验证机制。这样才能确保系统的数据安全和业务稳定。

重复提交是开发中经常遇到的问题。用户可能因为页面响应慢连续点击提交按钮,或者网络延迟时反复重试。这些情况会导致数据重复、业务混乱,比如生成重复订单、多次扣款等问题。

防止重复提交需要前后端配合。前端主要提升用户体验,后端才是真正的保障。

前端防止重复提交

前端方法能防止用户误操作,但不能完全依赖,因为可以通过工具绕过前端验证。

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

数据库约束

有唯一性要求

绝对可靠

只能防止最终重复

实践建议

  1. 前后端结合使用:前端防止误操作,后端保障数据安全
  2. 合理设置超时时间:一般5-10秒足够,避免影响正常操作
  3. 友好提示用户:不要直接报错,提示"操作进行中"或"请勿重复提交"
  4. 记录重复提交:监控重复提交情况,帮助优化系统
  5. 考虑幂等性:重要业务要实现幂等接口

防止重复提交是系统稳定性的基础保障。选择方案时要根据实际业务需求,有时候需要多种方案组合使用,才能达到最好的效果。

最重要的是,不要完全依赖前端的防护,后端必须要有相应的验证机制。这样才能确保系统的数据安全和业务稳定。

AI大模型学习福利

作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

一、全套AGI大模型学习路线

AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获取

二、640套AI大模型报告合集

这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

三、AI大模型经典PDF籍

随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。


因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

四、AI大模型商业化落地方案

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值