当前位置: 首页 > 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;

http://www.lryc.cn/news/138278.html

相关文章:

  • lvs-dr模式+keepalived
  • [C++]笔记-知识点总结
  • 1.RabbitMQ介绍
  • 软考高级系统架构设计师系列论文七十三:论中间件在SIM卡应用开发中的作用
  • 【Java架构-包管理工具】-Maven进阶(二)
  • 『C语言入门』分支和循环语句
  • 【给自己挖个坑】三维视频重建(NSR技术)-KIRI Engine
  • Chrome历史版本下载和Selenium驱动版本下载
  • 联合注入步骤
  • 后端项目开发:整合redis缓存
  • 美国访问学者签证好办吗?
  • Linux之基础IO文件系统讲解
  • Django主要特点
  • element-ui中的el-table合并单元格
  • 自组织地图 (SOM) — 介绍、解释和实现
  • Arduino程序设计(四)按键消抖+按键计数
  • Scrum Guide Chinese Simplified.pdf
  • Module not found: Error: Can‘t resolve ‘vue-pdf‘ in ‘xxx‘
  • ELK之LogStash介绍及安装配置
  • docker学习(1)
  • UE5 Niagara基础知识讲解
  • 缓存穿透、缓存击穿和缓存雪崩
  • 自动化编排工具Terraform介绍(一)
  • zhm_real/MotionPlanning运动规划库中A*算法源码详细解读
  • SpringMVC中Controller层获取前端请求参数的几种方式
  • 记Flask-Migrate迁移数据库失败的两个Bug——详解循环导入问题
  • 在线求助。。npm i 报错,连公司内部网,无法连外网
  • TCP/UDP/IP协议简介
  • 写点感想3:关于本人近期的说明与一点感受
  • opencv-全景图像拼接