阿里P7告訴你SpringBoot如何防止重複提交?

有兩種防止重複提交:javascript

  1. 禁用提交按鈕
  2. 發出請求令牌/ ID:

禁用提交按鈕

咱們能夠在函數調用HTTP請求以前禁用提交按鈕,並在完成HTTP響應後再次啓用它。該技術對於須要很長時間才能完成的過程(超過5秒)是有效的。因爲不耐煩而沒法得到結果,用戶沒法再次單擊n'。此外,咱們可能會顯示一個正在Loading裝載進度,以得到良好的體驗。html

<!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中有專門語句防止二次提交)java

重點來了:jquery

Spring Boot中如何發出請求令牌/ ID

這種技術實際上更復雜,更難實現,可是因爲一個好的框架(如Spring Boot)使這更容易。在咱們開始代碼實現以前,讓咱們先討論一下這個機制;web

  • 加載表單頁面時,發出新的requestId
  • 在調用後端服務以前將已發出的requestId發送到HTTP頭
  • 後端服務標識requestId是否已註冊
  • 若是requestId已經註冊,那麼咱們能夠將其標記爲違規請求

咱們來開始代碼。這裏是個人JavaScript中的示例代碼,用於發出新的requestId。ajax

$(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:spring

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。json

實際上,咱們能夠在識別requestId時修改如何發佈新令牌和邏輯。由於這個過程很是簡單,咱們須要一些東西(requestId)來識別已經請求過的東西。後端

寫在最後:

既然看到這裏了,以爲筆者寫的還不錯的就點個贊,加個關注唄!點關注,不迷路,持續更新!!!

相關文章
相關標籤/搜索