2023-08-24 10:19:21來源:Spring全家桶實戰(zhàn)案例源碼
環(huán)境:Springboot3.0.5
什么是接口防重接口防重是指在一定時間內(nèi)只允許執(zhí)行一次接口請求。這是為了防止由于重復提交和重復處理產(chǎn)生重復數(shù)據(jù)或相應錯誤。實現(xiàn)接口防重可以采用以下方法:
使用唯一標識符:在請求中包含一個唯一標識符(例如請求token),然后在對應接口判斷該唯一值在一定時間內(nèi)是否被消費過,如果已被消費,則拒絕該請求。使用時間戳、計數(shù)器等機制:記錄請求的時間或次數(shù),并在一定范圍內(nèi)拒絕重復請求。采用Spring AOP理念:實現(xiàn)請求的切割,在請求執(zhí)行到某個方法或某層時,開始攔截并進行防重處理。這些方法有助于確保系統(tǒng)的一致性和穩(wěn)定性,防止數(shù)據(jù)的重復提交和處理。
(資料圖片)
API接口的冪等性和防重性是兩個不同的概念,盡管它們在某些方面有重疊之處。
冪等性冪等性是指一個操作或API請求,無論執(zhí)行一次還是多次,結(jié)果都是相同的。在API設計中,冪等性是一種非常重要的屬性,因為它確保了在重試或并發(fā)請求時,系統(tǒng)狀態(tài)不會出現(xiàn)不一致的情況。在實現(xiàn)冪等性時,通常采用以下方法:
在請求中包含一個唯一標識符(例如請求ID),以便在處理請求時能夠識別和防止重復處理。使用樂觀鎖或悲觀鎖機制來保證數(shù)據(jù)的一致性。對于更新操作,可以通過比較新舊數(shù)據(jù)來判斷是否有變化,只有當數(shù)據(jù)發(fā)生改變時才執(zhí)行更新操作。防重性防重性是指在一定時間內(nèi)只允許執(zhí)行一次操作或請求。它主要用于防止重復提交和重復處理。與冪等性不同,防重性主要關(guān)注的是防止數(shù)據(jù)重復,而冪等性則關(guān)注任何多次執(zhí)行的結(jié)果都是相同的。技術(shù)實現(xiàn)方式1:通過AOP方式自定義注解@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface PreventDuplicate { /** * 唯一標識通過header傳遞時的key * * @return */ String header() default "token" ; /** * 唯一標識通過請求參數(shù)傳遞時的key * * @return */ String param() default "token" ;}
自定義AOP切面@Component@Aspectpublic class PreventDuplicateAspect { public static final String PREVENT_PREFIX_KEY = "prevent:" ; private final StringRedisTemplate stringRedisTemplate ; private final HttpServletRequest request ; public PreventDuplicateAspect(StringRedisTemplate stringRedisTemplate, HttpServletRequest request) { this.stringRedisTemplate = stringRedisTemplate ; this.request = request ; } @Around("@annotation(prevent)") public Object preventDuplicate(ProceedingJoinPoint pjp, PreventDuplicate prevent) throws Throwable { String key = prevent.header() ; String value = null ; if (key != null && key.length() > 0) { value = this.request.getHeader(key) ; } else { key = prevent.param() ; if (key != null && key.length() > 0) { value = this.request.getParameter(key) ; } } if (value == null || "".equals(value.trim())) { return "非法請求" ; } // 拼接rediskey String prevent_key = PREVENT_PREFIX_KEY + value ; // 判斷redis中是否存在當前請求中攜帶的唯一標識數(shù)據(jù), 刪除成功則存在 Boolean result = this.stringRedisTemplate.delete(prevent_key) ; if (result != null && result.booleanValue()) { return pjp.proceed() ; } else { return "請不要重復提交" ; } } }
生成唯一標識接口@RestController@RequestMapping("/generate")public class GenerateController { private final StringRedisTemplate stringRedisTemplate ; public GenerateController(StringRedisTemplate stringRedisTemplate) { this.stringRedisTemplate = stringRedisTemplate ; } @GetMapping("/token") public String token() { String token = UUID.randomUUID().toString().replace("-", "") ; // 將生成的token存入redis中,設置有效期5分鐘 this.stringRedisTemplate.opsForValue().setIfAbsent(PreventDuplicateAspect.PREVENT_PREFIX_KEY + token, token, 5 * 60, TimeUnit.SECONDS) ; return token ; } }
業(yè)務接口@RestController@RequestMapping("/prevent")public class PreventController { @PreventDuplicate @GetMapping("/index") public Object index() { return "index success" ; } }
測試先調(diào)用生成唯一接口獲取token值
圖片
調(diào)用業(yè)務接口,攜帶token值
第一次訪問, 正常
再次訪問
方式2:通過攔截器實現(xiàn)自定義攔截器@Componentpublic class PreventDuplicateInterceptor implements HandlerInterceptor { private final StringRedisTemplate stringRedisTemplate ; public PreventDuplicateInterceptor(StringRedisTemplate stringRedisTemplate) { this.stringRedisTemplate = stringRedisTemplate ; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod hm) { if (hm.hasMethodAnnotation(PreventDuplicate.class)) { PreventDuplicate pd = hm.getMethodAnnotation(PreventDuplicate.class) ; String key = pd.header() ; String value = null ; if (key != null && key.length() > 0) { value = request.getHeader(key) ; } else { key = pd.param() ; if (key != null && key.length() > 0) { value = request.getParameter(key) ; } } if (value == null || "".equals(value.trim())) { response.setContentType("text/plain;charset=utf-8") ; response.getWriter().println("非法請求") ; return false ; } // 拼接rediskey String prevent_key = PreventDuplicateAspect.PREVENT_PREFIX_KEY + value ; // 判斷redis中是否存在當前請求中攜帶的唯一標識數(shù)據(jù), 刪除成功則存在 Boolean result = this.stringRedisTemplate.delete(prevent_key) ; if (result != null && result.booleanValue()) { return true ; } else { response.setContentType("text/plain;charset=utf-8") ; response.getWriter().println("請不要重復提交") ; return false ; } } } return true ; } }
配置攔截器@Componentpublic class PreventWebConfig implements WebMvcConfigurer { private final PreventDuplicateInterceptor duplicateInterceptor ; public PreventWebConfig(PreventDuplicateInterceptor duplicateInterceptor) { this.duplicateInterceptor = duplicateInterceptor ; } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(this.duplicateInterceptor).addPathPatterns("/**") ; } }
測試獲取token
第一次請求
再次請求
完畢?。?!
關(guān)鍵詞:
環(huán)境:Springboot3 0 5什么是接口防重接口防重是指在一定時間內(nèi)只允許
我們知道RocketMQ主要分為消息生產(chǎn)、存儲(消息堆積)、消費三大塊領(lǐng)域
公攤問題,關(guān)鍵還在預售制,期房,套內(nèi),開發(fā)商,預售制,公攤面積,商品房現(xiàn)房
近日,上市公司回購股份掀起高潮?! ?月23日晚間,A股回購大軍繼續(xù)擴
比利時一男子占卜自己將死于空難,回家閉門不出,被失控飛機砸死,飛機,
國臺辦21日宣布即日起暫停臺灣芒果輸入大陸,臺灣一些媒體視為大陸叫停
由寧浩執(zhí)導、劉德華領(lǐng)銜主演的電影《紅毯先生》將亮相第48屆多倫多國際
中國地震臺網(wǎng)正式測定:8月23日18時40分在遼寧大連市普蘭店區(qū)(北緯39
很多人對男人三十五歲后感覺身體越來越差,男人三十五不是很了解那具體
隨著物聯(lián)網(wǎng)技術(shù)的迅速發(fā)展,越來越多的設備和傳感器被連接到互聯(lián)網(wǎng),產(chǎn)
隨著云計算和現(xiàn)代應用開發(fā)的快速發(fā)展,容器化技術(shù)已經(jīng)成為了構(gòu)建、部署
導讀1、要多關(guān)幾個月,才有可能性加入你。2、特別是一些高屬性武將,加
河北省水利廳和河北省氣象局8月23日13時聯(lián)合發(fā)布山洪災害氣象預警:預
喜大普奔!微軟把Python弄進Excel了!搭建一個機器學習天氣預測模型,
8月19日,“河海智慧水利1號芯”BX100E-HHU在“2023中國軟件產(chǎn)教融合生