Spring Boot (一) 校驗表單重複提交
一、前言
在某些情況下,由於網速慢,用戶操作有誤(連續點擊兩下提交按鈕),頁面卡頓等原因,可能會出現表單數據重複提交造成數據庫保存多條重複數據。
存在如上問題可以交給前端解決,判斷多長時間內不能再次點擊保存按鈕,當然,如果存在聰明的用戶能夠繞過前端驗證,後端更應該去進行攔截處理,下面小編將基於SpringBoot 2.1.8.RELEASE
環境通過AOP切面
+ 自定義校驗註解
+ Redis緩存
來解決這一問題。
二、Spring Boot 校驗表單重複提交操作
1、pom.xml
中引入所需依賴
<!-- ================== 校驗表單重複提交所需依賴 ===================== -->
<!-- AOP依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、application.yml
中引入Redis配置
spring:
redis:
# Redis數據庫索引(默認為0)
database: 0
# Redis服務器地址
host: 127.0.0.1
# Redis服務器連接端口
port: 6379
timeout: 6000
# Redis服務器連接密碼(默認為空)
# password:
jedis:
pool:
max-active: 1000 # 連接池最大連接數(使用負值表示沒有限制)
max-wait: -1 # 連接池最大阻塞等待時間(使用負值表示沒有限制)
max-idle: 10 # 連接池中的最大空閑連接
min-idle: 5 # 連接池中的最小空閑連接
3、自定義註解 @NoRepeatSubmit
// 作用到方法上
@Target(ElementType.METHOD)
// 運行時有效
@Retention(RetentionPolicy.RUNTIME)
public @interface NoRepeatSubmit {
/**
* 默認時間3秒
*/
int time() default 3 * 1000;
}
4、AOP 攔截處理
注:這裏redis存儲的key
值可由個人具體業務靈活發揮,這裏只是示例
ex:單用戶登錄情況下可以組合token + url請求路徑
,多個用戶可以同時登錄的話,可以再加上ip地址
@Slf4j
@Aspect
@Component
public class NoRepeatSubmitAop {
@Autowired
RedisUtil redisUtil;
/**
* <p> 【環繞通知】 用於攔截指定方法,判斷用戶表單保存操作是否屬於重複提交 <p>
*
* 定義切入點表達式: execution(public * (…))
* 表達式解釋: execution:主體 public:可省略 *:標識方法的任意返回值 任意包+類+方法(…) 任意參數
*
* com.zhengqing.demo.modules.*.api : 標識AOP所切服務的包名,即需要進行橫切的業務類
* .*Controller : 標識類名,*即所有類
* .*(..) : 標識任何方法名,括號表示參數,兩個點表示任何參數類型
*
* @param pjp:切入點對象
* @param noRepeatSubmit:自定義的註解對象
* @return: java.lang.Object
*/
@Around("execution(* com.zhengqing.demo.modules.*.api.*Controller.*(..)) && @annotation(noRepeatSubmit)")
public Object doAround(ProceedingJoinPoint pjp, NoRepeatSubmit noRepeatSubmit) {
try {
HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
// 拿到ip地址、請求路徑、token
String ip = IpUtils.getIpAdrress(request);
String url = request.getRequestURL().toString();
String token = request.getHeader(Constants.REQUEST_HEADERS_TOKEN);
// 現在時間
long now = System.currentTimeMillis();
// 自定義key值方式
String key = "REQUEST_FORM_" + ip;
if (redisUtil.hasKey(key)) {
// 上次表單提交時間
long lastTime = Long.parseLong(redisUtil.get(key));
// 如果現在距離上次提交時間小於設置的默認時間 則 判斷為重複提交 否則 正常提交 -> 進入業務處理
if ((now - lastTime) > noRepeatSubmit.time()) {
// 非重複提交操作 - 重新記錄操作時間
redisUtil.set(key, String.valueOf(now));
// 進入處理業務
ApiResult result = (ApiResult) pjp.proceed();
return result;
} else {
return ApiResult.fail("請勿重複提交!");
}
} else {
// 這裡是第一次操作
redisUtil.set(key, String.valueOf(now));
ApiResult result = (ApiResult) pjp.proceed();
return result;
}
} catch (Throwable e) {
log.error("校驗表單重複提交時異常: {}", e.getMessage());
return ApiResult.fail("校驗表單重複提交時異常!");
}
}
}
5、其中用到的Redis工具類
由於太多,這裏就不直接貼出來了,可參考文末給出的案例demo源碼
三、測試
在需要校驗的方法上加上自定義的校驗註解@NoRepeatSubmit
即可
@RestController
public class IndexController extends BaseController {
@NoRepeatSubmit
@GetMapping(value = "/index", produces = "application/json;charset=utf-8")
public ApiResult index() {
return ApiResult.ok("Hello World ~ ");
}
}
這裏重複訪問此index
api請求以模擬提交表單測試
第一次訪問
多次刷新此請求,則提示請勿重複提交!
四、總結
實現思路
- 首先利用
AOP切面
在進入方法前攔截
進行表單重複提交校驗邏輯處理 - 通過
Redis
的key-value鍵值對
存儲需要的邏輯判斷數據【ex:key存儲用戶提交表單的api請求路徑,value存儲提交時間】 邏輯處理
:
第一次提交時存入相應數據到redis中
當再次提交保存時從redis緩存中取出上次提交的時間與當前操作時間做判斷,
如果當前操作時間距離上次操作時間在我們設置的’判斷為重複提交的時間(3秒內)’則為重複提交直接返回重複提交提示語句或其它處理,
否則為正常提交,進入業務方法處理…
補充
如果api遵從的是嚴格的Restful風格
即@PostMapping
用於表單提交操作,則可不用自定義註解方式去判斷需要校驗重複提交的路徑,直接在aop切面攔截該請求路徑後,通過反射拿到該方法上的註解是否存在@PostMapping
如果存在則是提交表單的api,即進行校驗處理,如果不存在即是其它的@GetMapping
、@PutMapping
、@DeleteMapping
操作…
本文案例demo源碼
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※為什麼 USB CONNECTOR 是電子產業重要的元件?
※收購3c,收購IPHONE,收購蘋果電腦-詳細收購流程一覽表
※網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!
※想要讓你的商品在網路上成為最夯、最多人討論的話題?
※高價收購3C產品,價格不怕你比較
※想知道最厲害的台北網頁設計公司推薦、台中網頁設計公司推薦專業設計師”嚨底家”!!