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

防止表单重复提交的几种方式,演示一个自定义注解方式的实现

防止表单重复提交的几种方式,演示一个自定义注解方式的实现

  • 一、防止表单重复提交的几种方式
    • 方式一:Token 机制
    • 方式二:去重表(主要是利用 MySQL 的唯一索引机制来实现的)
    • 方式三:Redis 的 setnx
    • 方式四:设置状态字段
    • 方式五:锁机制
    • 方式六:自定义注解
  • 二、自定义注解方式的实现
    • 1. 准备工作,解决请求参数为JSON时,采用IO流读取,只能请求一次的问题
    • 2. 封住一个RedisCache简化使用
    • 3. 完善RepeatSubmitInterceptor拦截器,解析注解,判断是否重复提交
    • 4. 测试注解

一、防止表单重复提交的几种方式

方式一:Token 机制

  • 客户端请求服务端,获取一个 token,每一次请求都获取到一个全新的 token( token 会有一个超时时间),将 token 存入 redis 中,然后将 token 返回给客户端。
  • 客户端将来携带刚刚返回的 token 去请求一个接口。
  • 服务端收到请求后,分为两种情况:
    • 如果 token 在 redis 中,直接删除该 token,然后继续处理业务请求。
    • 如果 token 不在 redis 中,说明 token 过期或者当前业务已经执行过了,那么此时就不执行业务逻辑。
      特点:实现简单,但是多了一个获取 token 的过程。

方式二:去重表(主要是利用 MySQL 的唯一索引机制来实现的)

  • 客户端请求服务端,服务端将这次的请求信息(请求地址、参数等)存入到一个 MySQL 去重表中,这个去重表要根据这次请求的某个特殊字段建立唯一索引或者主键索引。
  • 判断是否插入成功:
    • 成功:继续完成业务功能。
    • 失败:表示业务已经执行过了,这次就不执行业务了。
      问题:MySQL 的容错性会影响业务、高并发环境可能效率低。

方式三:Redis 的 setnx

  • 客户端请求服务端,服务端将能代表本次请求唯一性的业务字段,通过 setnx 的方式存入 redis,并设置超时时间。
  • 判断 setnx 是否成功:
    • 成功:继续处理业务。
    • 失败:表示业务已经执行过了。

方式四:设置状态字段

  • 给要处理的数据设置一个状态字段。

方式五:锁机制

  • 乐观锁:数据库中增加版本号字段,每次更新都根据版本号来判断。
  • 更新之前先去查询要更新记录的版本号,第二步更新的时候,将版本号也作为查询条件。
select version from xxx where id = xxx;
update xxx set xxx=xxx where xxx=xxx and version=xxx;
  • 悲观锁, 假设每一次拿数据都会被修改,所以直接上排他锁就行了。
start;
select * from xxx where xxx for update;
update xxx
commit;

方式六:自定义注解

  • 将当前请求的地址参数缓存起来,下次再来一个请求时,去判断和缓存中的请求是否完全一样,一样的话并且小于规定的时间间隔,则认为是重复提交。

二、自定义注解方式的实现

1. 准备工作,解决请求参数为JSON时,采用IO流读取,只能请求一次的问题

  • 我们现在要使用拦截器拦截请求缓存的地址参数等信息,但是如果请求参数为JSON,拦截器拦截之后,接口就无法再一次获取了,所以先要解决这个问题。
  • 前边文章已经写过,点击跳转:如何解决请求参数为JSON时,采用IO流读取,只能请求一次的问题?

2. 封住一个RedisCache简化使用

@Component
public class RedisCache {@AutowiredRedisTemplate redisTemplate;public <T> void setCacheObject(final String key, final T value, Integer timeout, final TimeUnit timeUnit) {redisTemplate.opsForValue().set(key, value, timeout, timeUnit);}public <T> T getCacheObject(final String key) {ValueOperations<String,T> valueOperations = redisTemplate.opsForValue();return valueOperations.get(key);}
}

3. 完善RepeatSubmitInterceptor拦截器,解析注解,判断是否重复提交

详细步骤见代码注释:

@Component
public class RepeatSubmitInterceptor implements HandlerInterceptor {public static final String REPEAT_PARAMS = "repeat_params";public static final String REPEAT_TIME = "repeat_time";public static final String REPEAT_SUBMIT_KEY = "repeat_submit_key";public static final String HEADER = "Authorization";@AutowiredRedisCache redisCache;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//所以的controller方法都会被封装成HandlerMethodif (handler instanceof HandlerMethod){//分析注解HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();RepeatSubmit repeatSubmit = method.getAnnotation(RepeatSubmit.class);//如果注解存在&&请求重复if (repeatSubmit != null){if (isRepeatSubmit(request,repeatSubmit)){//拦截返回错误信息HashMap<String, Object> map = new HashMap<>();map.put("status",500);map.put("message",repeatSubmit.message());response.setContentType("application/json;charset=utf-8");response.getWriter().write(new ObjectMapper().writeValueAsString(map));return false;}}}return true;}private boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit repeatSubmit) {//获取请求参数字符串String nowParams = "";//RepeatableReadRequestWrapper 说明是JSON格式if (request instanceof RepeatableReadRequestWrapper){try {nowParams = ((RepeatableReadRequestWrapper) request).getReader().readLine();} catch (IOException e) {e.printStackTrace();}}//否则说明参数是key-value格式if (StringUtils.isEmpty(nowParams)){try {nowParams = new ObjectMapper().writeValueAsString(request.getParameterMap());} catch (JsonProcessingException e) {e.printStackTrace();}}//包装参数和当前时间HashMap<String, Object> nowDataMap = new HashMap<>();nowDataMap.put(REPEAT_PARAMS,nowParams);nowDataMap.put(REPEAT_TIME,System.currentTimeMillis());//获取请求信息,组装keyString requestURI = request.getRequestURI();String header = request.getHeader(HEADER);String cacheKey = REPEAT_SUBMIT_KEY + requestURI + header.replace("Bearer ","");//根据key查找redisObject cacheObject = redisCache.getCacheObject(cacheKey);if (cacheObject != null){//这里说明不是第一次,判断是否为重复请求(参数、时间)Map<String, Object> cacheMap = (Map<String, Object>) cacheObject;if (compareParams(cacheMap, nowDataMap) && compareTime(cacheMap, nowDataMap, repeatSubmit.interval())){return true;}}//到这里说明是第一次访问redisCache.setCacheObject(cacheKey,nowDataMap,repeatSubmit.interval(), TimeUnit.MILLISECONDS);return false;}private boolean compareTime(Map<String, Object> cacheMap, HashMap<String, Object> nowDataMap, int interval) {Long nowTime = (Long) nowDataMap.get(REPEAT_TIME);Long cacheTime = (Long) cacheMap.get(REPEAT_TIME);if (nowTime - cacheTime < interval) {return true;}return false;}private boolean compareParams(Map<String, Object> cacheMap, HashMap<String, Object> nowDataMap) {String cacheParams = (String) cacheMap.get(REPEAT_PARAMS);String nowParams = (String) nowDataMap.get(REPEAT_PARAMS);return nowParams.equals(cacheParams);}}

4. 测试注解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
到这里,一个自定义注解方式的防重就实现完了,点击跳转源码仓库地址。

相关文章:

防止表单重复提交的几种方式,演示一个自定义注解方式的实现

防止表单重复提交的几种方式&#xff0c;演示一个自定义注解方式的实现 一、防止表单重复提交的几种方式方式一&#xff1a;Token 机制方式二&#xff1a;去重表&#xff08;主要是利用 MySQL 的唯一索引机制来实现的&#xff09;方式三&#xff1a;Redis 的 setnx方式四&#…...

《基于智能手机采集的PPG信号预测血管老化》阅读笔记

目录 一、论文摘要 二、论文十问 Q1: Q1论文试图解决什么问题&#xff1f; Q2: 这是否是一个新的问题&#xff1f; Q3: 这篇文章要验证一个什么科学假设&#xff1f; Q4: 有哪些相关研究&#xff1f;如何归类&#xff1f;谁是这一课题在领域内值得关注的研究员&#xff1f…...

【大数据-调度工具】dolphinscheduler安装和遇到的问题

1.安装 安装步骤按照官网安装即可 官网&#xff1a;DolphinScheduler | 文档中心 (apache.org) 版本&#xff1a;3.1.5 2.踩坑记录 Q1.大文件无法上传 问题描述&#xff1a; 在资源中心中上传文件选择完大文件夹之后&#xff0c;选择确认之后确认按钮转了几圈圈之后就没…...

滑动轨迹生成的思路和代码分享-测试可过极验 90%机率

如有技术侵权、可联系本人下架 由于极验采用人工智能的方式对滑动的轨迹进行的验证,因此如果我们比较随意的生成鼠标滑动轨迹基本是肯定被封的,因此我们要详细分析一下鼠标轨迹的规律, 通之前介绍的调试手段,手工滑动滑块,获取到鼠标滑动轨迹的集合数组如下: [[-37,-41…...

【Linux】项目自动化构建工具make/makefile

&#x1f3d6;️作者&#xff1a;malloc不出对象 ⛺专栏&#xff1a;Linux的学习之路 &#x1f466;个人简介&#xff1a;一名双非本科院校大二在读的科班编程菜鸟&#xff0c;努力编程只为赶上各位大佬的步伐&#x1f648;&#x1f648; 目录 前言一、make/makefile的背景二、…...

【系分范文】论软件需求获取技术以及应用

目录 论题论题介绍论文要点理论素材准备范文摘要正文论题 论软件需求获取技术以及应用 论题介绍 软件需求是指用户对新系统在功能、行为、性能、设计约束等方面的期望。软件需求获取是一个确定和理解不同的项目干系人的需求和约束的过程。需求获取是否科学、准备充分,对获取…...

vue2.0中post请求

vue2.0中post请求 三种格式&#xff1a;在vue中axois的用法&#xff1a;1、 multipart/form-data类型2、 x-www-form-urlencoded类型3、 application/json类型 三种格式&#xff1a; ○ Content-Type:x-www-form-urlencoded ○ Content-Type:multipart/form-data ○ Content…...

MySQL双写缓冲区(Doublewrite Buffer)

本文已收录至Github&#xff0c;推荐阅读 &#x1f449; Java随想录 文章目录 摘要为什么需要Doublewrite BufferDoublewrite Buffer原理Doublewrite Buffer相关参数总结 摘要 InnoDB是MySQL中一种常用的事务性存储引擎&#xff0c;它具有很多优秀的特性。其中&#xff0c;Dou…...

免费开源的Umi-OCR 文字识别工具

大家好&#xff0c;我是小寻&#xff0c;欢迎关注公众号:工具优选&#xff0c;免费领取优质项目源码和常用工具&#xff0c;还可以加入我的交流群! 如今&#xff0c;在日常生活和工作中&#xff0c;我们经常需要捕捉屏幕截图并识别其中的文本信息。比如别人给你发资料时直接发…...

如何让微信小程序弹窗滚动条设置在最上面

最近发现一个事情搞得很烦&#xff0c;微信小程序的弹窗内容可以滚动的时候&#xff0c;要保证每一次打开都在最上面&#xff0c;研究了一下终于发现了怎么解决 第一步 首先得把你的弹窗里面的内容用scroll-view标签包起来&#xff0c;像这样 <scroll-view style"hei…...

c语言-指针

指针详解 ​ 这段时间在看 Linux内核&#xff0c;深觉C语言功底不扎实&#xff0c;很多代码都看不太懂&#xff0c;深入学习巩固C语言的知识很有必要。先从指针开始。 什么是指针 ​ C语言里&#xff0c;变量存放在内存中&#xff0c;而内存其实就是一组有序字节组成的数组&…...

Jenkins集成SonarQube实现代码质量检查

文章目录 一、前提配置1.1 安装及配置SonarQube Scanner插件1.2 配置SonarQube servers 二、非流水线集成SonarQube1.1 配置非流水线任务 三、流水线集成SonarQube 一、前提配置 1.1 安装及配置SonarQube Scanner插件 (1) 点击【系统管理】>【插件管理】>【可选插件】搜…...

2023 谷歌I/O发布会新AI,PALM 2模型要反超GPT-4,一雪前耻!

文章目录 1 前言2 Google I/O 发布者大会3 PaLM 2模型3 Bard项目4 其他AI工具4.1 AI 图片编辑 Magic Editor4.2 Duet AI 办公4.3 Universal Translator 翻译工具4.4 Google 沉浸式导航4.5 Google 搜索引擎 5 讨论 1 前言 每年必看两大会&#xff0c;苹果发布会和谷歌发布会&am…...

MySQL和Redis如何保证数据一致性?

前言 由于缓存的高并发和高性能已经在各种项目中被广泛使用&#xff0c;在读取缓存这方面基本都是一致的&#xff0c;大概都是按照下图的流程进行操作&#xff1a; 但是在更新缓存方面&#xff0c;是更新完数据库再更新缓存还是直接删除缓存呢&#xff1f;又或者是先删除缓存再…...

Markdown使用(超详细)

&#xff08;HBuilderX&#xff09; 掌握md及HBuilderX对md的强大支持。如果没有点右键设置自动换行&#xff0c;可按Alt滚轮横向滚动查看。 很多人只把markdown用于网络文章发表&#xff0c;这糟蹋了markdown。 markdown不止是HTML的简化版&#xff0c;更重要的是txt的升级版…...

yolov5实现扑克牌识别的产品化过程

文章目录 介绍项目下载硬件准备软件环境素材获取自行获取素材网盘获取图片标注模型训练窗口截图窗口截图(HWND)桌面截图wgc方法最终采用的方式WGC使用方法如何保存灰度图片python 如何加载dll库图片推理扑克牌逻辑ui编写模型加密软件授权软件加密软件打包安装包制作...

第07讲:Java High Level Client,读写 ES 利器

SkyWalking OAP 后端可以使用多种存储对数据进行持久化&#xff0c;例如 MySQL、TiDB 等&#xff0c;默认使用 ElasticSearch 作为持久化存储&#xff0c;在后面的源码分析过程中也将以 ElasticSearch 作为主要存储进行分析。 ElasticSearch 基本概念 本课时将快速介绍一下 E…...

dockerfile暴力处理配置文件外提

前言&#xff1a; 一般来说&#xff0c;springboot打成的jar运行时&#xff0c;同目录/config目录下放application.yml文件会被进行加载&#xff0c;然后通过设置docker映射出宿主机即可做到配置文件外配的效果&#xff0c;但很多时候别的配置文件做不到这种效果&#xff0c;说…...

如何快速给出解释——正交矩阵子矩阵的特征值的模必然不大于1

Memory 首先快速回忆一下正交矩阵的定义&#xff1a; A为n阶实矩阵&#xff0c;且满足A‘AE或是说AA’E&#xff0c;那么A为正交矩阵。 &#xff08;啊&#xff0c;多么简洁的定义&#xff09; 其次快速想到它的性质&#xff1a; ① 实特征值必然 或 其他复数…...

c语言-位运算

位运算小结 ​ 位运算不管是在C语言中&#xff0c;或者其他语言&#xff0c;都是经常会用到的&#xff0c;所以本文也就不固定以某种语言来举例子了&#xff0c;原始点就从0、1开始。位运算主要包括按位与(&)、按位或(|)、按位异或(^)、取反(~)、左移(<<)、右移(>…...

Redis 内存淘汰与过期策略

引言Redis 作为内存数据库&#xff0c;内存资源有限&#xff0c;必须妥善处理内存占用问题。本文梳理两种核心机制&#xff1a;淘汰策略决定内存达到上限时如何移除数据&#xff0c;涵盖 noeviction、LRU、LFU 等多种算法及其实现细节&#xff1b;过期策略&#xff08;惰性删除…...

计算机春考-系统管理与服务器配置-01安装Windows Server 2012 R2操作系统

活动1 创建虚拟机1. 单击VMware 主界面中的【创建新的虚拟机】2. 进入【新建虚拟机向导】界面&#xff0c;选中【典型&#xff08;推荐&#xff09;】按钮设置配置类型&#xff0c;点击【下一步】3. 在安装客户机操作系统界面选择【稍后安装操作系统】&#xff0c;单击【下一步…...

文书妙笔专为公文写作打造,让写材料效率翻倍

不同于市面上通用型写作工具&#xff0c;文书妙笔每一项功能都为公文写作量身定制&#xff0c;真正实现“写材料效率翻倍”。作为专为公文写作打造的平台&#xff0c;平台内置40W优质公文范文库&#xff0c;每日持续更新&#xff0c;更整合了最新政策表述、基层工作案例和规范金…...

资深大模型工程师详细讲解:RAG召回率优化三重微调实战

✅ 一、核心策略再解构&#xff1a;从“三层次”到“五维协同链路”原有“数据-索引-查询”三层结构非常精准&#xff0c;但为了更贴近企业级复杂场景&#xff0c;我们进一步抽象为 五维协同链路&#xff1a;维度关键目标是否可微调微调切入点1. 数据生成质量构建高质量正负样本…...

AI技术原理--AI上下文窗口:为什么AI没有真正的记忆

99%的人都理解错了一个问题&#xff1a;AI真的会记得你吗&#xff1f; 很多人信誓旦旦跟我说&#xff1a;昨天我跟GPT聊了好久&#xff0c;今天一打开还能接着聊&#xff0c;这不是记忆是什么&#xff1f; 但如果我告诉你&#xff0c;它根本就不记得你——你是不是直接懵了&…...

【NotebookLM 使用教程】 Slides 指令库:11套通用PPT风格Prompt(含中英文对照)

前言适合谁&#xff1a;不想从零写提示词&#xff0c;想直接套用成熟的 PPT 风格结构。你会获得什么&#xff1a;11 套通用风格指令&#xff08;英文为主&#xff0c;含中文备用&#xff09;&#xff0c;可直接粘贴到 NotebookLM Slides 的描述/自定义。建议阅读顺序&#xff1…...

4个步骤掌握python-for-android:Python移动应用跨平台部署指南

4个步骤掌握python-for-android&#xff1a;Python移动应用跨平台部署指南 【免费下载链接】python-for-android Turn your Python application into an Android APK 项目地址: https://gitcode.com/gh_mirrors/py/python-for-android 1. 问题&#xff1a;Python移动开发…...

3分钟焕新网易云音乐:BetterNCM Installer插件框架一键部署方案

3分钟焕新网易云音乐&#xff1a;BetterNCM Installer插件框架一键部署方案 【免费下载链接】BetterNCM-Installer 一键安装 Better 系软件 项目地址: https://gitcode.com/gh_mirrors/be/BetterNCM-Installer BetterNCM Installer是一款专为网易云音乐PC版设计的开源插…...

一键搞定完整网页截图:Chrome扩展终极解决方案

一键搞定完整网页截图&#xff1a;Chrome扩展终极解决方案 【免费下载链接】full-page-screen-capture-chrome-extension One-click full page screen captures in Google Chrome 项目地址: https://gitcode.com/gh_mirrors/fu/full-page-screen-capture-chrome-extension …...

多模态情报分析awesome-osint:文本图像视频融合处理终极指南

多模态情报分析awesome-osint&#xff1a;文本图像视频融合处理终极指南 开源情报&#xff08;OSINT&#xff09;正在经历一场革命性的变革&#xff0c;而awesome-osint项目正是这场变革的引领者。这个精心策划的资源集合汇集了最先进的多模态情报分析工具&#xff0c;让你能够…...