springboot 订单重复提交_Spring Boot (一) 校验表单重复提交
一、前言
在某些情况下,由于网速慢,用户操作有误(连续点击两下提交按钮),页面卡顿等原因,可能会出现表单数据重复提交造成数据库保存多条重复数据。
存在如上问题可以交给前端解决,判断多长时间内不能再次点击保存按钮,当然,如果存在聪明的用户能够绕过前端验证,后端更应该去进行拦截处理,下面小编将基于 SpringBoot 2.1.8.RELEASE 环境通过 AOP切面 + 自定义校验注解 + Redis缓存 来解决这一问题。
二、Spring Boot 校验表单重复提交操作
1、pom.xml 中引入所需依赖
org.springframework.boot
spring-boot-starter-aop
org.springframework.boot
spring-boot-starter-data-redis
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;
/**
*
【环绕通知】 用于拦截指定方法,判断用户表单保存操作是否属于重复提交
*
* 定义切入点表达式: 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源码
相关文章:

智能会议白板系统每日开发记录
智能会议白板系统,在开发过程中,整个项目期限内,每月,每周,每天要做的事情,作为组长的记录,多有不足之处,望指点。 转载于:https://www.cnblogs.com/mayijun/p/3458039.html

java.lang.OutOfMemoryError: PermGen space及其解决方法
PermGen space的全称是Permanent Generation space,是指内存的永久保存区域OutOfMemoryError: PermGen space从表面上看就是内存益出,解决方法也一定是加大内存。说说为什么会内存益出:这一部分用于存放Class和Meta的信息,Class在被 Load的时候被放入Per…

ceph-dencoder工具使用详解
文章目录简介使用decode命令用法encode简介 ceph-dencoder工具是一个序列化编码、解码并且打印ceph数据结构的工具。它主要用来调试和测试ceph不同版本之间的兼容性问题 该工具是由 ceph-common-12.2.1.06-0.el7.centos.x86_64 rpm包生成 本文章是根据ceph-12.2.1版本来描述改…

EBS fnd_global.apps_initialize
原型:fnd_global.apps_initialize(user_ID, Responsibility_id,Responsibility_application_id);作用:在数据库的会话中设置全局变量,和用户概要信息。参数获得:参数一,用户ID select user_idfrom fnd_userwhere user_…

js判断鼠标靠近屏幕最侧面的监听_threejs按鼠标位置缩放场景
threejs的orbitcontrol,默认的缩放模式为整体以target为中心进行缩放。有时候,我们想让场景按照鼠标位置进行缩放,体验起来就和地图的缩放一样,最直观的感觉就是整个场景会越来越靠近鼠标点的位置,而不是整体的缩放大小…

hibernate中多对多分解成一对多,
1,参考:http://blog.csdn.net/yaerfeng/article/details/6969632

C++ 函数参数 值传递与引用传递
以前我们在C语言中函数参数传递过程中,如果我们想要让当A函数作用域中的变量经过B函数处理之后的数值仍然在A函数中生效,这个时候函数参数的传递时需要引用方式去传递,方式如下: #include <stdio.h> //函数参数为指针&…

SharePoint 2013 图文开发系列之代码定义列表
在SharePoint的开发中,用Visual Studio自定义列表是经常会用到的,因为很多时候,我们并不会手动创建列表,而手动创建列表在测试服务器和正式机之间同步字段,也很麻烦,所以我们经常用代码来定义列表或者文档库…

arduino下载库出错_【arduino】DIY音乐播放器,arduino播放wav音乐,TRMpcm库测试及使用...
微信关注 “DLGG创客DIY”设为“星标”,重磅干货,第一时间送达。arduino特点库超多,想必大家都领教了,今天来分享一下之前玩过的TRMpcm库。这个库是干嘛用的?简单粗暴用arduino(这里特指arduino官方那几个板子uno、nan…

vim替换技巧4
、 转自:http://www.confay.com/2008/03/vim4.html [技巧一] 第一个是在VIM邮件列表中看到的,给出了一个如何统计文章字数的方法。 统计一个完整文件的字数,可以使用Unix下的wc工具,它能够统计一个文件的行数、单词数和字符数。 如…

spark1.x和2.xIterable和iterator兼容问题
1. spark 1.x 升级到spark 2.x 对于普通的spark来说,变动不大 : 1 举一个最简单的实例:spark1.x public static JavaRDD<String> workJob(JavaRDD<String> spark1Rdd) {JavaPairRDD<String, Integer> testRdd spark1Rdd.flatMapToPair(new PairFlatMapFunct…

C++ 拷贝构造函数和重载赋值运算符的区别
文章目录拷贝构造函数重载赋值运算符赋值运算符和拷贝构造函数最大区别是赋值运算符没有新的对象生成,而拷贝构造函数会生成新的对象。 为了更加形象 准确得描述 赋值运算符和拷贝构造函数得区别,将详细通过代码展示两者之间得差异。 拷贝构造函数 首先…

单元格内多个姓名拆分成一列_EXCEL拆分单元格中的姓名,这都不叫事儿
作者:祝洪忠 转自:Excel之家ExcelHome小伙伴们好啊,今天老祝和大家来分享一个数据整理的技巧。下面的表格形式,想必大家不会陌生吧:在这个表格内,同一个部门的人员名单都挤到一个单元格内。现在问题来了&am…
3.1 A Historical Perspective 历史观点
1.从1978年的8086到现在的2008年core i7 ,从29K个晶体管到781M个晶体管,地址线(也叫地址位长(bit long))8086只有20个地址线,1982年,MS-windows 使用80286平台开发了自己的windows。直到1985年,i386正式扩展…

idea中 maven打包时时报错User setting file does not exist C:\Users\lenevo\.m2\setting.xml,
第一种错误 :idea中 maven打包时时报错User setting file does not exist C:\Users\lenevo\.m2\setting.xml, 解决方案如下:将maven的安装目录\conf目录下的setting.xml拷贝到C:\Users\lenevo\.m2目录下即可。 第二种错误: This a…

关于部署osd过程中:Device is in use by a device-mapper mapping问题解决
ceph环境:12.2.1 使用古老的ceph-disk工具部署osd,仅仅prepare过程中就出现如上所示问题 Device is in use by a device-mapper mapping md127 解决方法如下: 由于device-mapper为系统自己的磁盘映射器,此时检查系统是否有逻辑卷 pvs lvs vg…

spyder一打开就卡了_欧姆龙plc 用 SD 卡上传/下载程序
以Nx102为例,NJ类似。使用 SD 卡将 Sysmac Studio 编写的程序传入 NX1P2 内; 使用 SD 卡对NX1P2 的程序进行备份, 查看备份的程序, 并把备份的程序传入另一台对应型号的 NX1P2 内。一、 使用 SD 卡将 Sysmac Studio 写的程序传入 …

Unity3D 中 2D_Toolkit插件下载 和 导入方法
Unity3D 中 2D_Toolkit插件下载 和 导入方法 1.你把下载来的包放到 安装目录:Editor\Standard Packages里面。 2.然后按ctrl9,进入asset store,等页面加载。 3.页面加载成功后,如果有账号,就登录,没有账号先…

Emacs 使用YASnippet
<?xml version"1.0" encoding"utf-8"?> Emacs 使用YASnippetUP | HOME Emacs 使用YASnippet Table of Contents 1 安装YASnippent2 安装 org-mode字典3 org-mode中使用教程4 YASnippet增加模板1 安装YASnippent $ cd ~/.emacs.d/plugins $ git c…

ruoyi后台管理系统分析(三)---admin包
三、admin包 --web包 -----controller包 -----------common包 CommonController.java------通用请求处理 package com.ruoyi.web.controller.common;import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import javax.servlet.http.HttpServletRequest;…

ceph-objectstore-tool工具使用详解
文章目录简介使用OSD相关操作PG相关操作对象相关操作总结简介 ceph-objectstore-tool工具,能够操作到ceph最底层的数据,包括pg,对象层级。它能够对底层pg以及对象相关数据进行获取、修改。并能够对一些问题pg和对象进行简单修复。所以使用该工具进行操作…

slf4j导入那个依赖_学习SPRINGBOOT结合日志门面SLF4J和日志实现LOGBACK的混合使用
一、此处主要介绍在springboot工程下如何使用 logback slf4j 进行日志记录。logback主要包含三个组成部分:Loggers(日志记录器)、Appenders(输出目的在)、Layouts(日志输出格式) slf4j :如jdbc一样,定义了一套接口,是一个日志门面…

linux下发布的执行文件崩溃的问题定位 心得一则
C Release版本发布到客户处执行时,如果程序崩溃,有什么办法能够快速的确认程序的问题呢? 如果能gdb调试的话,比较简单了,可以使用gdb命令,类似如下:gdb ##set args ****b mainr#eipx/10i 0xb736…

7 个漂亮的 JavaScript 的时间轴组件 [转]
时间轴:通过互联网技术,依据时间顺序,把一方面或多方面的 时间足迹事件串联起来,形成相对完整的记录体系,再运用图文的形式呈现给用户;时间轴可以运用于不同领域,最大的作用就是把过去的事物系统…

Python 学习笔记: 反射
Python 反射应用 例子1: class Person:def __init__(self, name, age):self.name nameself.age agedef show(self):print(%s \s age is %s%(self.name, self.age))alex Person(alex, 18) # alex.show() if hasattr(alex, show):getattr(alex, show)() 转载于:ht…

C++多线程:互斥变量 std::mutex
文章目录描述成员函数总结描述 头文件 <mutex>使用 std::mutex <variable>简介 mutex是一种多线程变成中的同步原语,它能够让共享数据不被多个线程同时访问,它不支持递归得对互斥对象上锁特点 用方线程从它成功调用 lock 或 try_lock 开始&…

vim替换字符串带斜杠_Vim、gvim操作替换
~ 回复 以下关键词 查看更多IC设计教程 ~目前支持的关键词有:Innovus ICC or IC CompilerDC or Design Compiler PT or PrimeTimeUser Guide or UG LedaVCS Formality工艺节点 …

SharePoint2010沙盒解决方案基础开发——关于TreeView树形控件读取列表数据(树形导航)的webpart开发及问题...
转:http://blog.csdn.net/miragesky2049/article/details/7204882 SharePoint2010沙盒解决方案基础开发——关于TreeView树形控件读取列表数据(树形导航)的webpart开发及问题 1、实现效果如下: 点击各个节点进入相应的链接 2、测试…

智能医疗?轻松实现!
每个中国人都及其关注养老和住房问题,在中国每个人都十分有存钱的意识。就目前来看,现在的医疗和住房保障还没有能够满足我们现在的需求,因而,存钱显得尤为重要。我们关注医疗,是因为每个人都会面临年老,每…

TokuDB vs Innodb 基准测试对比
随着业务的发展以及mysql存储数据量的越来越大,很多超大表不仅仅存储变的不易,维护也变得越来越困难,特别是频繁的ddl操作让运维变得痛苦不堪。当然表拆分可以解决类似的问题,但是对一个稳定的系统来说,表拆分对业务的…