表單重復提交:
由於用戶誤操作,多次點擊表單提交按鈕
由於網速等原因造成頁面卡頓,用戶重復刷新提交頁面
避免表單重復提交的方式:
1.頁面上的按鈕做防重復點擊操作
2.在數據庫中可以做唯一約束
3.利用token校驗重復提交
如何利用token校驗表單重復提交
思路:在表單提交前先請求后台獲取token,后台隨機生成token保存在session中,提交表單時在請求參數中帶上獲取的token即可,后台校驗token是否匹配。
token的獲取和校驗可以統一寫在AOP切面類中。
自定義注解
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Token { boolean save() default false ; boolean remove() default false ; }
切面類
@Aspect @Component public class TokenAspect { @SuppressWarnings("unused") @Before("within(@org.springframework.stereotype.Controller *) && @annotation(token)") public void testToken(final JoinPoint joinPoint, Token token){ try { if (token != null) { //獲取 joinPoint 的全部參數 Object[] args = joinPoint.getArgs(); HttpServletRequest request = null; HttpServletResponse response = null; for (int i = 0; i < args.length; i++) { //獲得參數中的 request && response if (args[i] instanceof HttpServletRequest) { request = (HttpServletRequest) args[i]; } if (args[i] instanceof HttpServletResponse) { response = (HttpServletResponse) args[i]; } } boolean needSaveSession = token.save(); if (needSaveSession){ String uuid = UUID.randomUUID().toString(); request.getSession().setAttribute( "token" , uuid); System.out.println("進入表單頁面,Token值為:"+uuid); } boolean needRemoveSession = token.remove(); if (needRemoveSession) { if (isRepeatSubmit(request)) { System.out.println("表單重復提交"); throw new FormRepeatException("表單重復提交"); } request.getSession(false).removeAttribute( "token" ); } } } catch (FormRepeatException e){ throw e; } catch (Exception e){ e.printStackTrace(); throw e; } } private boolean isRepeatSubmit(HttpServletRequest request) throws FormRepeatException { String serverToken = (String) request.getSession( false ).getAttribute( "token" ); if (serverToken == null ) { return true; } String clinetToken = request.getParameter( "token" ); if (clinetToken == null || clinetToken.equals("")) { return true; } if (!serverToken.equals(clinetToken)) { return true ; } System.out.println("校驗是否重復提交:表單頁面Token值為:"+clinetToken + ",Session中的Token值為:"+serverToken); return false ; } }
全局異常處理
@ControllerAdvice public class ControllerAdviceHandler { @ResponseBody @ExceptionHandler(value={com.irish.exception.FormRepeatException.class}) public String arithmeticExceptionHandler(Exception e){ return "您重復提交表單了!"; } }
自定義異常
public class FormRepeatException extends RuntimeException { private static final long serialVersionUID = 1L; public FormRepeatException(String message){ super(message);} }
controller層
@Controller public class URLController { /** * 獲取token,並將token保存在session中 * @return */ @Token(save = true) @RequestMapping("/queryToken") @ResponseBody public String getToken(HttpServletRequest request, HttpServletResponse response){ return (String) request.getSession().getAttribute("token"); } /** * 提交表單的地址,在AOP中檢查表單是否重復提交,將token刪除 * @param request * @param response * @return */ @Token(remove = true) @RequestMapping("/submitFrom") @ResponseBody public String removeToken(HttpServletRequest request, HttpServletResponse response){ return "success"; } }
項目結構: