有兩種防止重復(fù)提交:
- 禁用提交按鈕
- 發(fā)出請求令牌/ ID:
禁用提交按鈕
我們可以在函數(shù)調(diào)用HTTP請求之前禁用提交按鈕准谚,并在完成HTTP響應(yīng)后再次啟用它。該技術(shù)對于需要很長時間才能完成的過程(超過5秒)是有效的贯莺。由于不耐煩而無法獲得結(jié)果风喇,用戶無法再次單擊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”屬性作為開關(guān), (jquery中有專門語句防止二次提交)
重點來了:
Spring Boot中如何發(fā)出請求令牌/ ID
這種技術(shù)實際上更復(fù)雜耙考,更難實現(xiàn),但是由于一個好的框架(如Spring Boot)使這更容易潭兽。在我們開始代碼實現(xiàn)之前倦始,讓我們先討論一下這個機制;
- 加載表單頁面時,發(fā)出新的requestId
- 在調(diào)用后端服務(wù)之前將已發(fā)出的requestId發(fā)送到HTTP頭
- 后端服務(wù)標識requestId是否已注冊
- 如果requestId已經(jīng)注冊山卦,那么我們可以將其標記為違規(guī)請求
我們來開始代碼鞋邑。這里是我的JavaScript中的示例代碼,用于發(fā)出新的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項目中的示例代碼枚碗,我創(chuàng)建了一個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());
}
}
在此示例中,我使用應(yīng)用程序內(nèi)存來??存儲requestId铸本。對于認真的開發(fā)肮雨,我建議使用內(nèi)存數(shù)據(jù)庫,例如Redis箱玷。
實際上酷含,我們可以在識別requestId時修改如何發(fā)布新令牌和邏輯。因為這個過程非常簡單汪茧,我們需要一些東西(requestId)來識別已經(jīng)請求過的東西。
歡迎大家加入粉絲交流群:963944895限番,免費分享Spring框架舱污、Mybatis框架SpringBoot框架、SpringMVC框架弥虐、SpringCloud微服務(wù)扩灯、Dubbo框架、Redis緩存霜瘪、RabbitMq消息珠插、JVM調(diào)優(yōu)、Tomcat容器颖对、MySQL數(shù)據(jù)庫教學(xué)視頻及架構(gòu)學(xué)習(xí)思維導(dǎo)圖
寫在最后:
如需Java架構(gòu)資料捻撑,點關(guān)注,發(fā)簡信給我即可,先到先得顾患!
既然看到這里了番捂,覺得筆者寫的還不錯的就點個贊,加個關(guān)注唄江解!點關(guān)注设预,不迷路,持續(xù)更新@绾印1钫怼!