標籤:ble 擴充 package 開發 hold 詳細 factory err static
重複提交的問題在web開發中是很常碰到的一個問題,主要分為前端和後端兩種途徑解決,前端處理一般採用提交事件後,禁止使用者再次點擊提交按鈕,等待服務端結果再重設提交按鈕狀態。
本文著重介紹,通過java後端處理重複提交問題。開發環境是:spring boot 2.0+react+ant+dva,是主要流程思路:
以下是詳細步驟代碼:
1:用戶端登陸,服務端登陸成功後返回初始的表單令牌
package com.df.web.manager.security;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.util.UUID;/** * @類名稱: * @類描述: * @建立人 劉丹 * @建立時間 2018/6/23 * @最後修改人 劉丹. * @最後修改時間 2018/6/23. * @版本:1.0 */public class FormTokenUtil { public static String refreshFormToken(HttpServletRequest request, HttpServletResponse response) { String newFormToken = UUID.randomUUID().toString(); response.setHeader("formToken", newFormToken); request.getSession(true).setAttribute("formToken", newFormToken); return newFormToken; }}
2:前端擷取服務端返回的formToken
sessionStorage.setItem("formToken", resData.result.formToken);
3:在前端統一的request(fetch)的headers中增加表單token項
return request(serviceUrl, { method: "POST", headers: { ‘Accept‘: ‘application/json‘, ‘Content-Type‘: ‘application/json‘, ‘formToken‘: sessionStorage.getItem("formToken") }, body: data, credentials: ‘include‘ });
4:服務端使用aop技術攔截指定註解的Controller請求
package com.df.web.manager.aop;import com.df.web.manager.security.FormTokenUtil;import com.empiresoft.annotation.FormToken;import com.empiresoft.pojo.common.ActionResultGenerator;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Component;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * @類名稱: 表單重複提交攔截處理 * @類描述: * @建立人 劉丹 * @建立時間 2018/6/23 * @最後修改人 劉丹. * @最後修改時間 2018/6/23. * @版本:1.0 */@Aspect@Componentpublic class FormTokenAspect { private final Logger logger = LoggerFactory.getLogger(this.getClass()); /** * 對formToken註解的Action執行重複提交驗證 * * @param proceedingJoinPoint * @param formToken * @return */ @Around("@annotation(formToken)") public Object execute(ProceedingJoinPoint proceedingJoinPoint, FormToken formToken) { try { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); HttpServletResponse response = attributes.getResponse(); String strFormToken = request.getHeader("formToken"); if (strFormToken == null) { return ActionResultGenerator.errorResult("表單Token不可為空!"); } Object sessionFormToken = request.getSession(true).getAttribute("formToken"); if (sessionFormToken == null || !sessionFormToken.toString().equals(strFormToken)) { return ActionResultGenerator.errorResult("請勿重複提交資料!"); } //允許存取 Object o = proceedingJoinPoint.proceed(); //重設表單令牌 且寫入response 重設前端 表單令牌 FormTokenUtil.refreshFormToken(request, response); return o; } catch (Throwable e) { logger.error(e.getMessage()); return ActionResultGenerator.errorResult("發生異常!"); } }}
5:前端監控Response返回的資料中是否包含表單token項,如果包含則重設前端sessionStorage的表單token。
import fetch from ‘dva/fetch‘;import { message } from ‘antd‘;function parseJSON(response) { if (response.headers.get("formToken")) { sessionStorage.setItem("formToken", response.headers.get("formToken")) } return response.json();}function checkStatus(response) { if (response.status >= 200 && response.status < 300) { return response; }}/** * Requests a URL, returning a promise. * * @param {string} url The URL we want to request * @param {object} [options] The options we want to pass to "fetch" * @return {object} An object containing either "data" or "err" */export default function request(url, options) { return fetch(url, options) .then(checkStatus) .then(parseJSON) .then(data => ({ data })) .catch((err) => { });}
註解定義:
package com.empiresoft.annotation;import java.lang.annotation.*;/** * @類名稱:FormToken註解類 * @類描述:使用此註解 則表示需要驗證FormToken, 用於處理表單重複提交 * @建立人 劉丹 * @建立時間 2018/6/23 * @最後修改人 劉丹. * @最後修改時間 2018/6/23. * @版本:1.0 */@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface FormToken {}
標記需要重複提交驗證
@FormToken @RequestMapping(value = "/call_service", method = RequestMethod.POST) public ActionResult callServiceByPost(@RequestBody CallService callService) throws Exception { return OauthClientUtil.callUnifiedPlatformService(callService, SecurityUtil.getLoginUser(request), request); }
註:如需允許使用者不同的表單使用不同的表單token,只對同性質表單做重複提交驗證,可在前後端對token名稱"formToken"的命名做擴充處理。
java+react前後端分離項目處理重複提交問題