欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時(shí)間:2021年11月29日 10:36:03   作者:桐花思雨  
平時(shí)開發(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)重的問題,所以我們一般會(huì)對與數(shù)據(jù)庫有交互的接口進(jìn)行重復(fù)處理

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

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

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

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

單機(jī)

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

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

  • 攔截器攔截請求,將上一次請求的 url 和參數(shù)和這次的對比
  • 判斷,是否一致說明重復(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,表示沒有重復(fù)提交
        if (preUrlParams == null) { 
            httpServletRequest.getSession().setAttribute("repeatData", nowUrlParams);
            return false;
        // 否則,已經(jīng)有過提交了
        } 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 {

		//.....
	}
}

分布式

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

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

  • 使用分布式鎖 redisson 來嘗試獲取一把鎖
  • 如果成功獲取鎖,再獲取判斷 redis 中的 key 值是否存在(key 值自定義)
  • 如果 key 值不存在,則證明不是重復(fù)請求,再給 redis 中存入數(shù)據(jù)(使用 UUID 不重復(fù));反之,則證明是重復(fù)請求進(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("請勿重復(fù)提交");
    		} catch (Exception e) {
    			return new ResultMap().fail().message("獲取redisson分布式鎖異常");
    		}
		} finally {
    		// 釋放鎖
        	rLock.unlock();
    	}
		return null;
	}
}

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

總結(jié)

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

相關(guān)文章

  • SpringMVC?bean加載控制的實(shí)現(xiàn)分析

    SpringMVC?bean加載控制的實(shí)現(xiàn)分析

    SpringMVC是一種基于Java,實(shí)現(xiàn)了Web?MVC設(shè)計(jì)模式,請求驅(qū)動(dòng)類型的輕量級(jí)Web框架,即使用了MVC架構(gòu)模式的思想,將Web層進(jìn)行職責(zé)解耦?;谡埱篁?qū)動(dòng)指的就是使用請求-響應(yīng)模型,框架的目的就是幫助我們簡化開發(fā),SpringMVC也是要簡化我們?nèi)粘eb開發(fā)
    2023-02-02
  • 我總結(jié)的幾種@Transactional失效原因說明

    我總結(jié)的幾種@Transactional失效原因說明

    這篇文章主要是我總結(jié)的幾種@Transactional失效原因說明,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • WMTS中TileMatrix與ScaleDenominator淺析

    WMTS中TileMatrix與ScaleDenominator淺析

    這篇文章主要為大家介紹了WMTS中TileMatrix與ScaleDenominator淺析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • SpringBoot使用Mybatis-Generator配置過程詳解

    SpringBoot使用Mybatis-Generator配置過程詳解

    這篇文章主要介紹了SpringBoot使用Mybatis-Generator配置過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • synchronized底層實(shí)現(xiàn)原理

    synchronized底層實(shí)現(xiàn)原理

    這篇文章主要介紹了synchronized底層實(shí)現(xiàn)原理,想弄懂它的實(shí)現(xiàn)synchronized的原理,我們只能通過看編譯好的字節(jié)碼文件,下面文章的詳細(xì)內(nèi)容,我們就先從測試類開始吧,需要的小伙伴可以參考一下
    2022-01-01
  • mybatis-plus 返回部分字段的解決方式

    mybatis-plus 返回部分字段的解決方式

    這篇文章主要介紹了mybatis-plus 返回部分字段的解決方式,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • spring cloud hystrix 超時(shí)時(shí)間使用方式詳解

    spring cloud hystrix 超時(shí)時(shí)間使用方式詳解

    這篇文章主要介紹了spring cloud hystrix 超時(shí)時(shí)間使用方式,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • json如何解析混合數(shù)組對象到實(shí)體類的list集合里去

    json如何解析混合數(shù)組對象到實(shí)體類的list集合里去

    這篇文章主要介紹了json解析混合數(shù)組對象到實(shí)體類的list集合里去的操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Java單例模式簡單示例

    Java單例模式簡單示例

    這篇文章主要介紹了Java單例模式,結(jié)合實(shí)例形式簡單分析了java單例模式的定義與使用技巧,需要的朋友可以參考下
    2017-06-06
  • Java?數(shù)據(jù)結(jié)構(gòu)深入理解ArrayList與順序表

    Java?數(shù)據(jù)結(jié)構(gòu)深入理解ArrayList與順序表

    ArrayList?類是一個(gè)可以動(dòng)態(tài)修改的數(shù)組,與普通數(shù)組的區(qū)別就是它是沒有固定大小的限制,我們可以添加或刪除元素。ArrayList?繼承了?AbstractList?,并實(shí)現(xiàn)了?List?接口,順序表是將元素順序地存放在一塊連續(xù)的存儲(chǔ)區(qū)里,元素間的順序關(guān)系由它們的存儲(chǔ)順序自然表示
    2022-04-04

最新評(píng)論