谈谈如何防止表单重复提交

防止表单重复提交是我们在实际开发中经常需要考虑的事情,很多时候,我们可能在页面去做:当提交表单时,将按钮置为disabled,当获取到返回结果后将disabled去掉,这样保证当返回结果之前都无法再次发送请求。但是,很明显,只在前端做处理远远不够都,本文我们探讨都主要还是在后端做逻辑处理。

场景一 简单都防止手抖双击(连击)

用户’手抖’是常见不过的事情,手快也好,鼠标敏感也好,着急提交连续点击鼠标也罢,总的来说,这样的情况下,用户就会出现多次提交表单。造成的现象就是:假设提交报销申请,那么就会出现两单(或多单)内容完全一样的报销申请,很明显,这是不对的。

这里又分两种情况:

1、第一次请求结果还没有返回又发起第二次请求

前面介绍了,这种情况也可在前端处理,当然,我们在后端也得做逻辑判断。这种情况我们也是必须要处理的,如果网络或者接口比较慢,存在连击的话,那么将会产生非常多的相同的数据。

解决方案:
在提交表单前,先获取一个token,这个token在后端生成,比如可以根据jsessionId、登陆用户等可以定位当前用户以及请求等客户端的数据来生成。

String token = MD5(sessionId+userId);

这个token相当于一个标记,或者类似一把锁,拿到token的才能发送表单提交请求,没有拿到的都不发。

  • 前端发送Ajax请求,获取token。先从Redis取token,如果存在,则表示这个客户端有一个请求还没有返回,则不给token,返回错误code。如果不存在token,则生成token返回。
  • 前端如果获取到了错误码,表示之前有请求在处理,则不继续发送请求(根据情况可以决定是否进行提示);如果获取到了token,则表示可以进行表单到提交。后台拿到token,先校验Redis中是否存在,如果存在,则进行处理,比如保存报销单,逻辑处理完成后将token从Redis中移除。当然,如果不存在token,说明此次请求是不处理的。(使用aop实现完美)

2、第一次请求已经结束才发起的第二次请求

这种情况与前面一种其实不一样,当前两次请求其实是相互独立的,我提交了一次报销申请,我肯定还可以再次提交一次报销申请,如果我们做了上述的防止表单重复提交校验,那么这种情况下又需要做什么呢?

后端处理到前端获取到结果可能是存在网络延迟的,即使结果已经返回了,基于原来表单的数据,可能存在连击的情况,也就是说,可能将完全一模一样的表单数据再次进行了提交,并且不在第一次情况内,如果是报销单,那么我依然可能被保存了两条一模一样的报销申请。

解决方案:

我们以报销为例,如果短时间内提交相同内容的报销内容,我们是否可以认为是重复提交呢?对于报销来说,相同的报销,我们可以把具有相同报销金额、报销类型、报销说明等字段相同则认为相同(根据实际情况)。

  • 前端提交表单,我们根据关键字段的内容生成一个token(拥有相同的内容,token必然相同),我们去Redis中查询是否有此token:如果有,则表示表单算重复提交,本次请求不处理或者返回上次的结果;如果没有,则将token存于Redis。
  • 我们保存报销数据,处理完成后,我们将结果返回之前也存入Redis,这个和之前token一样,需要设置一个失效时间(比如5秒失效,5秒内不可以发送同样数据的表单)

这样,我们可以保证5秒内,不可能提交完全数据一样的表单并且都会被处理。

由于我们系统一般集群部署,所以需要使用到Redis。

© 2019 FunGa技术札记 All Rights Reserved. 本站访客数人次 本站总访问量
Theme by hiero