java+react前后端分离项目处理重复提交问题
Posted 一脸沧桑的刘先生
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java+react前后端分离项目处理重复提交问题相关的知识,希望对你有一定的参考价值。
重复提交的问题在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 @Component public 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前后端分离项目处理重复提交问题的主要内容,如果未能解决你的问题,请参考以下文章
SpringBoot + React 前后端分离多模块项目框架搭建流程
利用Spring boot+react快速搭建一个博客站点(前后端完全分离)
Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)四(微服务搭建-通用工具类-通用异常处理-自定义异常处理)