一、前言

在某些情况下,由于网速慢,用户操作有误(连续点击两下提交按钮),页面卡顿等原因,可能会出现表单数据重复提交造成数据库保存多条重复数据。

存在如上问题可以交给前端解决,判断多长时间内不能再次点击保存按钮,当然,如果存在聪明的用户能够绕过前端验证,后端更应该去进行拦截处理,下面小编将基于 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源码

springboot 订单重复提交_Spring Boot (一) 校验表单重复提交相关推荐

  1. Spring Boot中防表单重复提交以及拦截器登录检测

    目录 理论 演示 源码 理论 在用户登录后,如果按F5刷新会出现表单重复提交的问题,解决这个问题后,如果没有拦截器登录检测,就会造成,任意用户可以登录后台界面,所以要有拦截器登录检测. 相关的逻辑步骤 ...

  2. php token 表单重复提交,PHP生成token防止表单重复提交2个例子

    防止表单重复提交的解决方案非常的简单,我们下面两个例子都是生成一个随机的token验证用户是不是由我们站内提交并且进行重复验证即可实现了. 在网上搜索了一下有很多站长都这样说的 1.提交按钮置disa ...

  3. php 表单提交文件大小,PHP如何通过表单直接提交大文件详解

    PHP如何通过表单直接提交大文件详解 前言 我想通过表单直接提交大文件,django 那边我就是这么干的.而对于 php 来说,我认为尽管可以设置最大上传的大小,但最大也无法超过内存大小,因为它无法把 ...

  4. php 表单提交及验证 tp,ThinkPHP表单自动提交验证实例教程

    ThinkPHP表单自动提交验证实例教程 附加规则 配合验证规则使用,包括: function 使用函数验证,前面定义的验证规则是一个函数名 callback 使用方法验证,前面定义的验证规则是一个当 ...

  5. html表单通过js提交表单提交,JavaScript动态创建form表单并提交的实现方法

    本文实例讲述了JavaScript动态创建form表单并提交的实现方法.分享给大家供大家参考,具体如下: 页面布局有些复杂的情况下,可能需要在页面中动态创建一个 form,JavaScript 创建 ...

  6. 防止表单重复提交的简单有效的策略

    点击提交按钮两次. 点击刷新按钮. 使用浏览器后退按钮重复之前的操作,导致重复提交表单. 使用浏览器历史记录重复提交表单. 浏览器重复的HTTP请求.用户提交表单时可能因为网速的原因,或者网页被恶意刷 ...

  7. 防止Form表单重复提交的客户端及服务器端的方式

    参考:https://www.cnblogs.com/xdp-gacl/p/3859416.html 上文只是介绍了form表单提交,没有介绍form表单异步提交 上文只是介绍了单个Servlet的处 ...

  8. 使用ajax提交图片,提交已经注入文件的表单给后台上传图片 使用ajaxsubmit

    使用jquery form插件进行异步带文件的表单提交 引入form插件与jquery 的js文件后 获取表单的jq对象 然后.ajaxSubmit提交表单即可 实现添加品牌的异步表单提交 funct ...

  9. form表单同时提交带文本和图片的数据

    方法一:使用ajax异步提交 此种方法的好处是可以回调响应结果. html代码: <form action="${basePath}/save" method="p ...

最新文章

  1. python编写通讯录管理系统_一个简单的python程序实例(通讯录)
  2. ubuntu下连接mysql出现Access denied for user ‘rose‘@‘localhost‘ (using password: NO)的解决方法
  3. 查看mysql语句运行时间的2种方法
  4. jQuery学习整理 (12)
  5. Bash 脚本 set 命令教程
  6. linux2019内核版本发布,新兴的桌面发行版 Septor Linux 发布 2019 版
  7. android dex2oatd e,解決android項目Error:Execution failed for task ':app:dexDebug'.ebug'錯誤
  8. 鸿蒙生态与苹果生态有什么区别,华为想用鸿蒙统一生态,苹果直接用芯片大一统,走到华为前面?...
  9. 李开复《纽约时报》专栏:美国对中国AI的几大误解
  10. 知云文献翻译打不开_推荐一个很好用的阅读英文文献工具(研究僧看过来啦!!!)...
  11. mysql+int+类型如何模糊搜索_mysql全文模糊搜索MATCH AGAINST方法示例
  12. 计算机win7如何连接wifi网络,细说win7怎么共享wifi
  13. paypal开发整理(7)—Get Start
  14. (记录)golang获取mongo的ObjectId
  15. CMD执行SQL文件 SQL SERVER数据库
  16. Thinking -- CSS从根解决选择前一个兄弟元素
  17. 使用vue-cli搭建SPA项目
  18. Scratch之制作幸运抽奖机
  19. [advGAN]Generating Adversarial Examples With Adversarial Networks
  20. 【视频教程免费领取】聚焦Python分布式爬虫必学框架Scrapy 打造搜索引擎

热门文章

  1. Python中完整的机器学习数据科学课程
  2. gcc中-pthread和-lpthread的区别
  3. python pdb 基础调试
  4. 解决Eclipse添加新server时无法选择Tomcat7的问题
  5. VScode 插件、配置记录
  6. Lua生成Guid(uuid)
  7. html-css实例
  8. sqlserver任务导出Excle
  9. [轉]如果把HTML當成飾品....
  10. PHP动态编译出现Cannot find autoconf