美文网首页互联网科技Java高级架构Java学习笔记
阿里P7告诉你SpringBoot如何防止重复提交?

阿里P7告诉你SpringBoot如何防止重复提交?

作者: Java高级架构狮 | 来源:发表于2019-03-05 15:35 被阅读9次

    有两种防止重复提交:

    1. 禁用提交按钮
    2. 发出请求令牌/ ID:

    禁用提交按钮

    我们可以在函数调用HTTP请求之前禁用提交按钮,并在完成HTTP响应后再次启用它。该技术对于需要很长时间才能完成的过程(超过5秒)是有效的。由于不耐烦而无法获得结果,用户无法再次单击n'。此外,我们可能会显示一个正在Loading装载进度,以获得良好的体验。

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
            <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
    </head>
    
    <body>
        <form name="form-payment" id="form-payment">
            ...
        </form>
    
        <script type="text/javascript">
            $('#form-payment').submit(function (e) {
                e.preventDefault();
                $.ajax({
                    type: 'POST',
                    dataType : "json",
                    contentType: "application/json; charset=utf-8",
                    url: "#",
                    data: "{}",
                    beforeSend: function(){
                        $('#button-submit').attr('disabled', 'disable');
                    },
                    complete: function(){
                        $('#button-submit').removeAttr('disabled');
                    },
                    success: function (data) {
                            // do your success action
                    },
                    error: function () {
                            // do your error handler
                    }
                });
            });
        </script>
    </body>
    

    在beforeSend 和complete段,我添加“ disable”属性作为开关, (jquery中有专门语句防止二次提交)

    重点来了:

    Spring Boot中如何发出请求令牌/ ID

    这种技术实际上更复杂,更难实现,但是由于一个好的框架(如Spring Boot)使这更容易。在我们开始代码实现之前,让我们先讨论一下这个机制;

    • 加载表单页面时,发出新的requestId
    • 在调用后端服务之前将已发出的requestId发送到HTTP头
    • 后端服务标识requestId是否已注册
    • 如果requestId已经注册,那么我们可以将其标记为违规请求

    我们来开始代码。这里是我的JavaScript中的示例代码,用于发出新的requestId。

     $(document).ready(function () {
            var requestId = new Date().getTime(); // <--- issue new requestId every time page laoded 
           
            $('#form-payment').submit(function (e) {
                e.preventDefault();
                $.ajax({
                    type: 'POST',
                    dataType : "json",
                    contentType: "application/json; charset=utf-8",
                    headers: { "requestId" : requestId }, // <--- add requestId in header
                    url: "#",
                    data: "{}",
                    beforeSend: function(){
                        $('#button-submit').attr('disabled', 'disable');
                    },
                    complete: function(){
                        $('#button-submit').removeAttr('disabled');
                    },
                    success: function (data) {
                    
                    },
                    error: function () {
                    
                    }
                });
            });
        });
    

    这里是我的Spring Boot项目中的示例代码,我创建了一个Interceptor来处理requestId:

    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.ArrayList;
    import java.util.List;
    
    @Configuration
    public class Interceptor implements WebMvcConfigurer {
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new ViolationInterceptor()).addPathPatterns("/**");
        }
    
        public class ViolationInterceptor extends HandlerInterceptorAdapter {
            private List<String> requestIds = new ArrayList<>();
    
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                String requestId = request.getHeader("requestId");
    
                if (requestIds.contains(requestId))
                    throw new IllegalArgumentException("Violation Request; Reason requestId already registered");
    
                requestIds.add(requestId);
                return super.preHandle(request, response, handler);
            }
        }
    }
    

    Exception处理:

    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    
    @ControllerAdvice
    public class ExceptionAdvisor {
    
        @ExceptionHandler(IllegalArgumentException.class)
        ResponseEntity illegalArgumentExceptionHandler(IllegalArgumentException e){
            return ResponseEntity.ok(e.getMessage());
        }
    }
    

    在此示例中,我使用应用程序内存来​​存储requestId。对于认真的开发,我建议使用内存数据库,例如Redis。

    实际上,我们可以在识别requestId时修改如何发布新令牌和逻辑。因为这个过程非常简单,我们需要一些东西(requestId)来识别已经请求过的东西。

    欢迎大家加入粉丝交流群:963944895,免费分享Spring框架、Mybatis框架SpringBoot框架、SpringMVC框架、SpringCloud微服务、Dubbo框架、Redis缓存、RabbitMq消息、JVM调优、Tomcat容器、MySQL数据库教学视频及架构学习思维导图

    写在最后:

    如需Java架构资料,点关注,发简信给我即可,先到先得!
    既然看到这里了,觉得笔者写的还不错的就点个赞,加个关注呗!点关注,不迷路,持续更新!!!

    相关文章

      网友评论

        本文标题:阿里P7告诉你SpringBoot如何防止重复提交?

        本文链接:https://www.haomeiwen.com/subject/gyqouqtx.html