欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

解决表单重复提交

程序员文章站 2024-03-18 22:55:10
...

   记得前几次面试的时候,3家公司问了表单重复提交的问题,我回答的都不是特别好,虽然知道是个什么流程,但是因为只知道理论,所以面试官问的细一点就懵了

这次就写了一个demo测试一下之前的想法是否准确.

1   表单重复提交大部分情况是用户点击提交时,因为服务端处理数据太慢,没能及时返回结果给用户,用户重复的点击提交按钮,这个时候就会出现重复提交,这里我用Thread.sleep增加响应延迟

解决表单重复提交

 

2  用户提交表单成功,服务端处理完成并将结果响应给用户了.但是可能用户手滑一直f5请求刷新,这时候也会造成表单重复提交

解决表单重复提交

 

3  用户提交表单成功,服务端处理完成并将结果响应给用户了.但是可能用户手滑一直返回提交返回提交,这时候也会造成表单重复提交

解决表单重复提交


鉴于以上3种经常出现情况,都有相应的解决方案

1 js方案

提交按钮设置为不可用状态.  但是无法防止2,3种情况发生

<form action="${pageContext.request.contextPath}/fromResubmit/submit2" method="post" onsubmit="preEvent()">
    用户名:<input type="text" name="username">
    <input type="submit" value="提交" id="submit_id">
</form>
<script type="application/javascript">
    function preEvent() {
        document.getElementById("submit_id").setAttribute("disabled", "disabled");
    }
</script>

 

解决表单重复提交

2   当用户请求form页面时,在服务端生成token,并存放在当前用户session中和form页面表单的隐藏域中

当用户点击请求时必须将表单隐藏的token传入到服务端,然后进行两端token的验证,验证不同过打回去

 @RequestMapping("/doForm")
    public String doForm(HttpServletRequest request) {
        //创建token
        String token = UUID.randomUUID().toString();
        //在服务端session中保存token
        request.getSession().setAttribute("token", token);
        System.out.println("本次生成的token:" + token);
        //跳转到form页面
        return "/fromResubmit/form";
    }


    @RequestMapping(value = "/submit", produces = "text/html;charset=UTF-8")
    @ResponseBody
    public String submit(HttpServletRequest request) {
        //取出客户端表单中的token,如果表单中没有token则认为用户是重复提交
        String clientToken = request.getParameter("token");
        if (StringUtils.isEmpty(clientToken)) {
            return "请不要重复提交";
        }
        //取出服务端当前用户session中的token,如果为空则认为用户重复提交
        String serviceToken = (String) request.getSession().getAttribute("token");
        if (StringUtils.isEmpty(serviceToken)) {
            return "请不要重复提交";
        }
        //判断两个客户端和服务端我token是否相同,不相同认为用户重复提交
        if (!Objects.equals(clientToken, serviceToken)) {
            return "请不要重复提交";
        }

        //确定本次请求不是重复提交,移除session中的token
        request.getSession().removeAttribute("token");
        return "提交成功";
    }
<form action="${pageContext.request.contextPath}/fromResubmit/submit" method="post">
    <%--使用EL表达式取出存储在session中的token,并存放在表单隐藏域中--%>
    <input type="hidden" name="token" value="${token}"/>
    用户名:<input type="text" name="username">
    <input type="submit" value="提交">
</form>

解决表单重复提交


总结

有些基础的东西真的很值得深究啊,就拿这个表单重新提交 我发现我们公司的项目组没有一个有重复验证的   

之前我做的那个重复回款验证的  也是根据现有的数据用redis做的锁来验证重复提交的,之后再根据状态来验证是否打回去,所以完全不适用上面的方案

如果项目是集群部署的话  可以考虑用一个中间件来替换session,这样就不会出现重复提交的尴尬了

源码在此:https://github.com/zhouminsen/web