当前位置: 首页 > news >正文

springBoot防止重复提交

两种方法,
一种是后端实现,较复杂,要通过自定义注解和AOP以及Redis组合实现
另一种是前端实现,简单,只需通过js,设置过期时间,一定时间内,多次点击按钮只生效一次

后端实现

自定义注解+AOP+Redis

自定义注解

package com.wzw.config.anno;import java.lang.annotation.*;/*** 自定义注解防止表单重复提交*/
@Target(ElementType.METHOD) // 注解只能用于方法
@Retention(RetentionPolicy.RUNTIME) // 修饰注解的生命周期
@Documented
public @interface RepeatSubmit {/*** 防重复操作过期时间,默认1s*/long expireTime() default 1;
}

AOP

package com.wzw.config.aspect;import com.wzw.config.anno.RepeatSubmit;
import com.wzw.config.exception.CustomException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
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 java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;/*** 防止重复提交切面*/
@Slf4j
@Component
@Aspect
public class RepeatSubmitAspect {@Autowiredprivate RedisTemplate redisTemplate;/*** 定义切点*/@Pointcut("@annotation(com.wzw.config.anno.RepeatSubmit)")public void repeatSubmit() {}@Around("repeatSubmit()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();// 获取防重复提交注解RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);// 获取token当做keyString token = request.getHeader("token");if (StringUtils.isBlank(token)) {throw new RuntimeException("token不存在,请登录!");}String url = request.getRequestURI();/***  通过前缀 + url + token 来生成redis上的 key*  可以在加上用户id,小编这里没办法获取,大家可以在项目中加上*/String redisKey = "repeat_submit_key:".concat(url).concat(token);log.info("==========redisKey ====== {}",redisKey);if (!redisTemplate.hasKey(redisKey)) {redisTemplate.opsForValue().set(redisKey, redisKey, annotation.expireTime(), TimeUnit.SECONDS);try {//正常执行方法并返回return joinPoint.proceed();} catch (Throwable throwable) {redisTemplate.delete(redisKey);throw new Throwable(throwable);}} else {// 抛出异常throw new CustomException("请勿重复提交");}}
}

自定义异常类和全局异常处理

自定义异常类:CustomException

package com.wzw.config.exception;/*** 自定义异常*/
public class CustomException extends Exception {public CustomException() {super();}public CustomException(String message) {super(message);}
}

全局异常处理:CustomExceptionHandler

package com.wzw.config.exception;import com.wzw.base.pojo.Result;
import com.wzw.config.exception.CustomException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;@Slf4j
@RestControllerAdvice
public class CustomExceptionHandler {/*** 每当抛出CustomException异常,就会进入这里* @param e 自定义异常类* @return  返回值实体*/@ExceptionHandler(value = CustomException.class)@ResponseBodypublic Result handleCustomException(CustomException e){Result result=Result.init();result.setMsg(e.getMessage());result.setCode(0);return result;}}

响应实体:Result

package com.wzw.base.pojo;import lombok.Data;/*** 返回值响应实体*/
@Data
public class Result {private int code;private String msg;private Object data;public static Result init(){return new Result();}public static Result ok(){Result result=Result.init();result.code=1;return result;}public static Result ok(String msg){Result result=Result.ok();result.setMsg(msg);return result;}public static Result err(){Result result=Result.init();result.code=0;return result;}public static Result err(String msg){Result result=Result.err();result.setMsg(msg);return result;}
}

Redis

设置Redis配置数据源,然后创建 Redis配置类

package com.wzw.config.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory){RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);// 使用StringRedisSerializer来序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(serializer);// Hash的key也采用StringRedisSerializer的序列化方式template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(serializer);template.afterPropertiesSet();return template;}
}

使用

    /*** 防止重复提交测试* @return  响应实体* @throws CustomException  自定义异常类*/@RequestMapping("/testCustomerException")@ResponseBody@RepeatSubmit(expireTime = 10)  //不加expireTime,默认1s内,加参数可以重新设定防止重复提交的时间public Result testCustomerException() throws CustomException {Result result=Result.ok("请求成功");return result;}

可以看到第一次是请求成功,之后五秒都是提示请勿重复提交
在这里插入图片描述

前端实现

来源:https://blog.csdn.net/liangmengbk/article/details/127075604

/* 防止重复点击 */
let clickTimer = 0function clickThrottle() {var interval = 3000;//3秒钟之内重复点击只算一次点击let now = +new Date(); // 获取当前时间的时间戳let timer = clickTimer; // 记录触发事件的事件戳if (now - timer < interval) {// 如果当前时间 - 触发事件时的事件 < interVal,那么不符合条件,直接return false,// 不让当前事件继续执行下去return false;} else {// 反之,记录符合条件触发了事件的时间戳,并 return true,使事件继续往下执行clickTimer = now;return true;}
}

在需要的地方进行调用:

if(!clickThrottle()) return;

相关文章:

springBoot防止重复提交

两种方法&#xff0c; 一种是后端实现&#xff0c;较复杂&#xff0c;要通过自定义注解和AOP以及Redis组合实现 另一种是前端实现&#xff0c;简单&#xff0c;只需通过js&#xff0c;设置过期时间&#xff0c;一定时间内&#xff0c;多次点击按钮只生效一次 后端实现 自定义注…...

lvs-dr模式+keepalived

一&#xff0c;keepalived概述 Keepalived 是一个基于VRRP协议来实现的LVS服务高可用方案&#xff0c;可以解决静态路由出现的单点故障问题。 在一个LVS服务集群中通常有主服务器&#xff08;MASTER&#xff09;和备份服务器&#xff08;BACKUP&#xff09;两种角色的服务器&am…...

[C++]笔记-知识点总结

一.输入密码时候,隐藏密码 用函数getch(),头文件#include<conio.h>输入一个字符时候不会回显,getc会回显实现思路: 输入一个字符,由于不知道密码长度,所以设置为死循环,如果不是回车键,即将该字符添加到存放密码的数组里,顺便打印一个星号,如果输入的为回车键,由于getch…...

1.RabbitMQ介绍

一、MQ是什么&#xff1f;为什么使用它 MQ&#xff08;Message Queue&#xff0c;简称MQ&#xff09;被称为消息队列。 是一种用于在应用程序之间传递消息的通信方式。它是一种异步通信模式&#xff0c;允许不同的应用程序、服务或组件之间通过将消息放入队列中来进行通信。这…...

软考高级系统架构设计师系列论文七十三:论中间件在SIM卡应用开发中的作用

软考高级系统架构设计师系列论文七十三:论中间件在SIM卡应用开发中的作用 一、中间件相关知识点二、摘要三、正文四、总结一、中间件相关知识点 软考高级系统架构设计师:构件与中间件技术二、摘要 我曾于近期参与过一个基于SIM卡应用的开发项目,并在项目中担任系统分析的工作…...

【Java架构-包管理工具】-Maven进阶(二)

本文摘要 Maven作为Java后端使用频率非常高的一款依赖管理工具&#xff0c;在此咱们由浅入深&#xff0c;分三篇文章&#xff08;Maven基础、Maven进阶、私服搭建&#xff09;来深入学习Maven&#xff0c;此篇为开篇主要介绍Maven进阶知识&#xff0c;包含坐标、依赖、仓库、生…...

『C语言入门』分支和循环语句

文章目录 引言一、什么是语句&#xff1f;1.1表达式语句1.2赋值语句1.3函数调用语句1.4复合语句1.5空语句1.6控制语句 二、分支语句2.1 if语句2.1.1基本语法2.1.2使用else语句2.1.3嵌套if语句2.1.4多层if-else语句 2.2 switch语句2.2.1基本语法2.2.2示例2.2.3穿透 三、循环语句…...

【给自己挖个坑】三维视频重建(NSR技术)-KIRI Engine

文章目录 以下是我和AI的对话通过手机拍摄物体的视频&#xff0c;再根据视频生成三维模型&#xff0c;这个可实现吗我想开发类似上面的手机应用程序&#xff0c;如何开发呢 看了以上回答&#xff0c;还是洗洗睡吧NSR技术的实现原理是什么呢有案例吗我是名Java工程师&#xff0c…...

Chrome历史版本下载和Selenium驱动版本下载

Python自动化必备&#xff1a; Selenium驱动版本下载 http://chromedriver.storage.googleapis.com/index.html Chrome浏览器历史版本下载 https://www.slimjet.com/chrome/google-chrome-old-version.php...

联合注入步骤

使用场景&#xff1a; 有回显&#xff0c;可以看到某些字段的回显信息 像下面的有具体的回显信息 一、判断注入位点 在原始的id&#xff08;参数&#xff09;的输入后面添加额外的条件 如果and 11 有结果&#xff0c;and10没有结果输出&#xff0c; 就说明我们添加的额外条件…...

后端项目开发:整合redis缓存

因为各种场合比如门户和后台&#xff0c;需要不同的redis配置&#xff0c;我们在common包配置通用的reids配置。 1.新建service目录&#xff0c;建立RedisService服务接口&#xff0c;同时编写工具类实现该接口。 public interface RedisService {/*** 保存属性*/void set(Str…...

美国访问学者签证好办吗?

近年来&#xff0c;随着国际交流与合作的不断深入&#xff0c;许多人对于美国访问学者签证的办理情况产生了浓厚的兴趣。那么&#xff0c;美国访问学者签证到底好办吗&#xff1f;让知识人网小编带您一起了解一下。 首先&#xff0c;美国作为世界上的科研、教育和创新中心之一&…...

Linux之基础IO文件系统讲解

基础IO文件系统讲解 回顾C语言读写文件读文件操作写文件操作输出信息到显示器的方法stdin & stdout & stderr总结 系统文件IOIO接口介绍文件描述符fd文件描述符的分配规则C标准库文件操作函数简易模拟实现重定向dup2 系统调用在minishell中添加重定向功能 FILE文件系统…...

Django主要特点

Django 是一个开源的 Python Web 开发框架&#xff0c;它提供了一系列的工具和功能&#xff0c;帮助开发人员快速、高效地构建 Web 应用程序。 以下是 Django 的一些主要特点&#xff1a; 1. 强大的 ORM&#xff08;对象关系映射&#xff09;&#xff1a; Django 提供了一个…...

element-ui中的el-table合并单元格

描述&#xff1a; 在写项目的时候有时候会经常遇到把行和列合并起来的情况&#xff0c;因为有些数据是重复渲染的&#xff0c;不合并行列会使表格看起来非常的混乱&#xff0c;如下&#xff1a; 而我们想要的数据是下面这种情况&#xff0c;将重复的行进行合并&#xff0c;使表…...

自组织地图 (SOM) — 介绍、解释和实现

自组织地图 &#xff08;SOM&#xff09; — 介绍、解释和实现 一、说明 什么是SOM&#xff08;self orgnize map&#xff09;自组织地图&#xff0c;是GNN类似的图神经网络的概念。因为神经网络实质上可以解释为二部图的权重&#xff0c;因此无论GNN还是SOM都有共同的神经网络…...

Arduino程序设计(四)按键消抖+按键计数

按键消抖按键计数 前言一、按键消抖二、按键计数1、示例代码2、按键计数实验 参考资料 前言 本文主要介绍两种按键控制LED实验&#xff1a;第一种是采用软件消抖的方法检测按键按下的效果&#xff1b;第二种是根据按键按下次数&#xff0c;四个LED灯呈现不同的流水灯效果。 一…...

Scrum Guide Chinese Simplified.pdf

敏捷指南...

Module not found: Error: Can‘t resolve ‘vue-pdf‘ in ‘xxx‘

使用命令npm run serve时vue项目报错&#xff1a; Module not found: Error: Cant resolve vue-pdf in xxx 解决方案&#xff1a; 运行命令&#xff1a; npm install vue-pdf --save --legacy-peer-deps 即可解决。 再次顺利执行npm run serve...

ELK之LogStash介绍及安装配置

一、logstash简介 集中、转换和存储数据 Logstash 是免费且开放的服务器端数据处理管道&#xff0c;能够从多个来源采集数据&#xff0c;转换数据&#xff0c;然后将数据发送到您最喜欢的“存储库”中。 Logstash 能够动态地采集、转换和传输数据&#xff0c;不受格式或复杂度的…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…...

从面试角度回答Android中ContentProvider启动原理

Android中ContentProvider原理的面试角度解析&#xff0c;分为​​已启动​​和​​未启动​​两种场景&#xff1a; 一、ContentProvider已启动的情况 1. ​​核心流程​​ ​​触发条件​​&#xff1a;当其他组件&#xff08;如Activity、Service&#xff09;通过ContentR…...

书籍“之“字形打印矩阵(8)0609

题目 给定一个矩阵matrix&#xff0c;按照"之"字形的方式打印这个矩阵&#xff0c;例如&#xff1a; 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为&#xff1a;1&#xff0c;…...

shell脚本质数判断

shell脚本质数判断 shell输入一个正整数,判断是否为质数(素数&#xff09;shell求1-100内的质数shell求给定数组输出其中的质数 shell输入一个正整数,判断是否为质数(素数&#xff09; 思路&#xff1a; 1:1 2:1 2 3:1 2 3 4:1 2 3 4 5:1 2 3 4 5-------> 3:2 4:2 3 5:2 3…...