亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

springMVC如何防止表單重復(fù)提交詳解

 更新時(shí)間:2021年11月29日 10:36:03   作者:桐花思雨  
平時(shí)開(kāi)發(fā)的項(xiàng)目中經(jīng)常會(huì)遇到表單重復(fù)提交,造成數(shù)據(jù)重復(fù),增加服務(wù)器負(fù)載,嚴(yán)重甚至?xí)斐煞?wù)器宕機(jī),因此有效防止表單重復(fù)提交有一定的必要性,這篇文章主要給大家介紹了關(guān)于springMVC如何防止表單重復(fù)提交的相關(guān)資料,需要的朋友可以參考下

?前言

在系統(tǒng)中,有些接口如果重復(fù)提交,可能會(huì)造成臟數(shù)據(jù)或者其他的嚴(yán)重的問(wèn)題,所以我們一般會(huì)對(duì)與數(shù)據(jù)庫(kù)有交互的接口進(jìn)行重復(fù)處理

  • 首先可以在前端做一層控制。當(dāng)前端觸發(fā)操作時(shí),或彈出確認(rèn)界面,或 disable 禁用按鈕等等,但是這并不能徹底解決問(wèn)題。假設(shè)我們不是從客戶端提交,而是被其他的系統(tǒng)調(diào)用,還會(huì)遇到這種問(wèn)題
  • 為了徹底解決問(wèn)題,還需要在后端對(duì)接口做防重處理

一般會(huì)引起表單重復(fù)提交的場(chǎng)景

  • 在網(wǎng)絡(luò)延遲的情況下讓用戶有時(shí)間點(diǎn)擊多次 submit 按鈕導(dǎo)致表單重復(fù)提交
  • 表單提交后用戶點(diǎn)擊【刷新】按鈕導(dǎo)致表單重復(fù)提交
  • 用戶提交表單后,點(diǎn)擊瀏覽器的【后退】按鈕回退到表單頁(yè)面后進(jìn)行再次提交

防止表單重復(fù)提交

單機(jī)

實(shí)現(xiàn)的思路步驟

通過(guò)當(dāng)前用戶上一次請(qǐng)求的 url 和參數(shù),驗(yàn)證是否是重復(fù)的請(qǐng)求

  • 攔截器攔截請(qǐng)求,將上一次請(qǐng)求的 url 和參數(shù)和這次的對(duì)比
  • 判斷,是否一致說(shuō)明重復(fù)提交并記錄日志

代碼實(shí)現(xiàn)

創(chuàng)建自定義注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SameUrlData {

}

創(chuàng)建自定義攔截器

public class SameUrlDataInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            // 是否有 SameUrlData 注解
            SameUrlData annotation = method.getAnnotation(SameUrlData.class);
            if (annotation != null) {
            	// 如果重復(fù)相同數(shù)據(jù)
                if (repeatDataValidator(request)) {
                    response.sendRedirect("/error/409");
                    return false;
                } else {
                    return true;
                }
            }
            return true;
        } else {
            return super.preHandle(request, response, handler);
        }
    }

    /**
     * 驗(yàn)證同一個(gè)url數(shù)據(jù)是否相同提交,相同返回true
     */
    private boolean repeatDataValidator(HttpServletRequest httpServletRequest) {
        String params = JsonMapper.toJsonString(httpServletRequest.getParameterMap());
        String url = httpServletRequest.getRequestURI();
        Map<String, String> map = new HashMap<>();
        map.put(url, params);
        String nowUrlParams = map.toString();

        Object preUrlParams = httpServletRequest.getSession().getAttribute("repeatData");
        // 如果上一個(gè)數(shù)據(jù)為null,表示沒(méi)有重復(fù)提交
        if (preUrlParams == null) { 
            httpServletRequest.getSession().setAttribute("repeatData", nowUrlParams);
            return false;
        // 否則,已經(jīng)有過(guò)提交了
        } else { 
        	// 如果上次url+數(shù)據(jù) 和 本次url+數(shù)據(jù)相同,則表示重復(fù)添加數(shù)據(jù)
            if (preUrlParams.toString().equals(nowUrlParams)) { 
                return true;
            // 如果上次 url+數(shù)據(jù) 和 本次url加數(shù)據(jù)不同,則不是重復(fù)提交
            } else { 
                httpServletRequest.getSession().setAttribute("repeatData", nowUrlParams);
                return false;
            }
        }
    }
}

將自定義攔截器添加到容器

<mvc:interceptor>
	<mvc:mapping path="/**"/>
	<bean class="com.chinagdn.base.common.interceptor.SameUrlDataInterceptor"/>
</mvc:interceptor>

controller 層

@Controller
public class TestController {

	// 在 controller 層使用 @SameUrlData 注解即可
	@SameUrlData
	@RequestMapping(value = "save", method = RequestMethod.POST)
	public String save(@Valid LoginUser loginUser, Errors errors, RedirectAttributes redirectAttributes, Model model) throws Exception {

		//.....
	}
}

分布式

很顯然在分布式的情況下,使用上述方法已無(wú)法防止表單重復(fù)提交了;一者在分布式部署下 session 是不共享的,二者使用 上一次請(qǐng)求的 url 和參數(shù)和這次的對(duì)比 已無(wú)法對(duì)比請(qǐng)求的 url 和參數(shù)了。在此情況下,就可以使用 redisson 的分布式鎖來(lái)實(shí)現(xiàn)了

實(shí)現(xiàn)的思路步驟

  • 使用分布式鎖 redisson 來(lái)嘗試獲取一把鎖
  • 如果成功獲取鎖,再獲取判斷 redis 中的 key 值是否存在(key 值自定義)
  • 如果 key 值不存在,則證明不是重復(fù)請(qǐng)求,再給 redis 中存入數(shù)據(jù)(使用 UUID 不重復(fù));反之,則證明是重復(fù)請(qǐng)求進(jìn)行提交
  • 最后,釋放分布式鎖

代碼實(shí)現(xiàn)

Maven 依賴

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
	<groupId>org.redisson</groupId>
	<artifactId>redisson-spring-boot-starter</artifactId>
	<version>3.11.0</version>
</dependency>

配置文件配置 redis 信息

# redis 的配置
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=
spring.redis.database=0
spring.redis.timeout=2000
spring.redis.lettuce.pool.max-active=10
spring.redis.lettuce.pool.max-wait=2
spring.redis.lettuce.pool.min-idle=5
spring.redis.lettuce.pool.max-idle=10

RedissonClient 配置類

@Configuration
public class RedissonConfig {

	@Bean
	public RedissonClient redissonClient() {
		Config config = new Config();
		config.useSingleServer().setAddress("redis://127.0.0.1:6379");
		RedissonClient client = Redisson.create(config);
		return client;
	}
}

controller 層,偽代碼如下

@Controller
public class TestController {

	@Autowired
    private StringRedisTemplate stringRedisTemplate;

	@Autowired
	private RedissonClient redissonClient;

	@PostMapping(path="/registerLock")
	@ResponseBody
	public ResultMap registerLock(UserDto userDto) {
		RLock rLock = redissonClient.getLock(userDto.getUserName());   
		// redis中的key值
       	String key = userDto.getUserName() + "-redissonLock";
       	boolean resultLock = rLock.tryLock(30, 10, TimeUnit.SECONDS);
		if (resultLock) {
			try {        	
        		// 如果不存在key
        		if (!stringRedisTemplate.hasKey(key)) {
            		// 給redis中存入數(shù)據(jù)
            		stringRedisTemplate.opsForValue().set(key,UUID.randomUUID().toString(),10L,TimeUnit.SECONDS);
            		// .....................你的業(yè)務(wù)
            		
            	}
            	// 如果存在,則提示不可重復(fù)提交
            	return new ResultMap().fail().message("請(qǐng)勿重復(fù)提交");
    		} catch (Exception e) {
    			return new ResultMap().fail().message("獲取redisson分布式鎖異常");
    		}
		} finally {
    		// 釋放鎖
        	rLock.unlock();
    	}
		return null;
	}
}

分布式鎖防止表單重復(fù)提交參考:http://chabaoo.cn/article/230608.htm

總結(jié)

到此這篇關(guān)于springMVC如何防止表單重復(fù)提交的文章就介紹到這了,更多相關(guān)springMVC防止表單重復(fù)提交內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java基礎(chǔ)之初始化ArrayList時(shí)直接賦值的4種方式總結(jié)

    java基礎(chǔ)之初始化ArrayList時(shí)直接賦值的4種方式總結(jié)

    ArrayList是Java中的一個(gè)類,它是Java集合框架中的一部分,用于實(shí)現(xiàn)動(dòng)態(tài)數(shù)組,下面這篇文章主要給大家介紹了關(guān)于java基礎(chǔ)之初始化ArrayList時(shí)直接賦值的4種方式,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-07-07
  • IO 使用說(shuō)明介紹

    IO 使用說(shuō)明介紹

    本篇文章小編為大家介紹,IO 使用說(shuō)明介紹。需要的朋友參考下
    2013-04-04
  • 在Java中對(duì)List進(jìn)行分區(qū)的實(shí)現(xiàn)方法

    在Java中對(duì)List進(jìn)行分區(qū)的實(shí)現(xiàn)方法

    在本文中,我們將說(shuō)明如何將一個(gè)列表拆分為多個(gè)給定大小的子列表,也就是說(shuō)在 Java 中如何對(duì)List進(jìn)行分區(qū),文中有詳細(xì)的代碼示例供大家參考,需要的朋友可以參考下
    2024-04-04
  • Java中利用POI優(yōu)雅的導(dǎo)出Excel文件詳解

    Java中利用POI優(yōu)雅的導(dǎo)出Excel文件詳解

    這篇文章主要給大家介紹了關(guān)于Java中如何利用POI優(yōu)雅的導(dǎo)出Excel文件的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • ReentrantLock實(shí)現(xiàn)原理詳解

    ReentrantLock實(shí)現(xiàn)原理詳解

    本文將對(duì)ReentrantLock實(shí)現(xiàn)原理進(jìn)行詳細(xì)的介紹,具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-02-02
  • MyBatis動(dòng)態(tài)SQL特性詳解

    MyBatis動(dòng)態(tài)SQL特性詳解

    動(dòng)態(tài)SQL可以省略很多拼接SQL的步驟,使用類似于JSTL方式,下面這篇文章主要給大家介紹了關(guān)于Mybatis動(dòng)態(tài)SQL特性的相關(guān)資料,文字通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-11-11
  • IDEA如何導(dǎo)入已有Maven項(xiàng)目

    IDEA如何導(dǎo)入已有Maven項(xiàng)目

    導(dǎo)入Maven項(xiàng)目到IDEA時(shí),經(jīng)常遇到問(wèn)題,本文記錄正確步驟,首先創(chuàng)建空項(xiàng)目,然后選擇Import Module from external model并選擇Maven,勾選所需profiles和Maven項(xiàng)目,最后設(shè)置Project SDK,這樣配置后,項(xiàng)目結(jié)構(gòu)將符合預(yù)期,僅顯示一個(gè)Module
    2024-11-11
  • 解決Process.getInputStream()阻塞的問(wèn)題

    解決Process.getInputStream()阻塞的問(wèn)題

    這篇文章主要介紹了解決Process.getInputStream()阻塞的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • SpringCloud的@RefreshScope 注解你了解嗎

    SpringCloud的@RefreshScope 注解你了解嗎

    這篇文章主要介紹了Spring Cloud @RefreshScope 原理及使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-09-09
  • Java中Object類的理解和使用

    Java中Object類的理解和使用

    Object類是java.lang包下的核心類,Object類是所有類的父類,何一個(gè)類時(shí)候如果沒(méi)有明確的繼承一個(gè)父類的話,那么它就是Object的子類,本文將通過(guò)代碼示例詳細(xì)介紹一下Java中Object類的理解和使用,需要的朋友可以參考下
    2023-06-06

最新評(píng)論