Spring MVC Flash Attribute 解决POST/Redirect/GET模式问题缺陷
来源:互联网 发布:java运行无法加载主类 编辑:程序博客网 时间:2024/06/07 07:20
1.描述
最近需要解决项目遗留下来的表单重复提交的问题,因为提交方式为表单提交(从入行开始好像我一直使用的都是Ajax提交),第一个想到的解决方案就是POST/Redirect/GET,但是因为有强迫症不喜欢在URL中进行传值,经寻找找到了通过Spring MVC提供的闪存来解决传参问题。
方案详见:http://viralpatel.net/blogs/spring-mvc-flash-attribute-example/
译文详见:http://www.oschina.net/translate/spring-mvc-flash-attribute-example?p=1#comments
2.问题
经实践发现第一次进入重定向页面传值没有问题,当重定向页面进行F5刷新之后传递的参数就会取不到。
3.分析
为什么在页面F5刷新之后传递的参数就会取不到呢,第一反应就是存储在闪存中的值被移除,经阅读源码发现发现移除的规则为:
1).当闪存中的值在3分钟之内未进行匹配则自动移除
2).当闪存中的值被匹配之后自动移除,匹配方式为URI匹配
闪存移除数据规则代码(org.springframework.web.servlet.support.AbstractFlashMapManager)
@Override public final FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response) { List<FlashMap> allFlashMaps = retrieveFlashMaps(request); if (CollectionUtils.isEmpty(allFlashMaps)) { return null; } if (logger.isDebugEnabled()) { logger.debug("Retrieved FlashMap(s): " + allFlashMaps); } //寻找闪存中过期的数据 List<FlashMap> mapsToRemove = getExpiredFlashMaps(allFlashMaps); //匹配缓存中的数据(匹配规则为URL==重定向的地址) FlashMap match = getMatchingFlashMap(allFlashMaps, request); if (match != null) { //把匹配到的数据加入移除的集合中 mapsToRemove.add(match); } //移除闪存的数据 if (!mapsToRemove.isEmpty()) { if (logger.isDebugEnabled()) { logger.debug("Removing FlashMap(s): " + mapsToRemove); } Object mutex = getFlashMapsMutex(request); if (mutex != null) { synchronized (mutex) { allFlashMaps = retrieveFlashMaps(request); if (allFlashMaps != null) { allFlashMaps.removeAll(mapsToRemove); updateFlashMaps(allFlashMaps, request, response); } } } else { allFlashMaps.removeAll(mapsToRemove); updateFlashMaps(allFlashMaps, request, response); } } return match; }
设置闪存数据过期时间代码(org.springframework.web.servlet.support.AbstractFlashMapManager)
@Override public final void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response) { if (CollectionUtils.isEmpty(flashMap)) { return; } String path = decodeAndNormalizePath(flashMap.getTargetRequestPath(), request); flashMap.setTargetRequestPath(path); if (logger.isDebugEnabled()) { logger.debug("Saving FlashMap=" + flashMap); } //设置过期时间(getFlashMapTimeout() = 180) flashMap.startExpirationPeriod(getFlashMapTimeout()); Object mutex = getFlashMapsMutex(request); if (mutex != null) { synchronized (mutex) { List<FlashMap> allFlashMaps = retrieveFlashMaps(request); allFlashMaps = (allFlashMaps != null ? allFlashMaps : new CopyOnWriteArrayList<FlashMap>()); allFlashMaps.add(flashMap); updateFlashMaps(allFlashMaps, request, response); } } else { List<FlashMap> allFlashMaps = retrieveFlashMaps(request); allFlashMaps = (allFlashMaps != null ? allFlashMaps : new LinkedList<FlashMap>()); allFlashMaps.add(flashMap); updateFlashMaps(allFlashMaps, request, response); } }
过期时间计算规则(org.springframework.web.servlet.FlashMap)
/** * Start the expiration period for this instance. * @param timeToLive the number of seconds before expiration */ public void startExpirationPeriod(int timeToLive) { this.expirationTime = System.currentTimeMillis() + timeToLive * 1000; }
闪存数据匹配规则代码
/** * Whether the given FlashMap matches the current request. * Uses the expected request path and query parameters saved in the FlashMap. */ protected boolean isFlashMapForRequest(FlashMap flashMap, HttpServletRequest request) { //重定向地址 String expectedPath = flashMap.getTargetRequestPath(); if (expectedPath != null) { //当前地址 String requestUri = getUrlPathHelper().getOriginatingRequestUri(request); //进行匹配 if (!requestUri.equals(expectedPath) && !requestUri.equals(expectedPath + "/")) { return false; } } UriComponents uriComponents = ServletUriComponentsBuilder.fromRequest(request).build(); MultiValueMap<String, String> actualParams = uriComponents.getQueryParams(); MultiValueMap<String, String> expectedParams = flashMap.getTargetRequestParams(); for (String expectedName : expectedParams.keySet()) { List<String> actualValues = actualParams.get(expectedName); if (actualValues == null) { return false; } for (String expectedValue : expectedParams.get(expectedName)) { if (!actualValues.contains(expectedValue)) { return false; } } } return true; }
4.结论
通过Spring MVC Flash Attribute 解决POST/Redirect/GET模式是存在页面F5刷新取不到传递的参数问题,所以此方案行不通。
5.备注
Spring MVC Flash Attribute的默认实现是存储在sesion当中的,
实现类为org.springframework.web.servlet.support.SessionFlashMapManager
可以继承抽象类org.springframework.web.servlet.support.AbstractFlashMapManager自定义实现
- Spring MVC Flash Attribute 解决POST/Redirect/GET模式问题缺陷
- ASP.NET MVC - 使用Post, Redirect, Get (PRG)模式
- spring mvc 中文乱码 post与get的方法解决
- 解决Sping Mvc中post、get请求中文乱码问题
- Post/Redirect/Get pattern
- Post/Redirect/Get pattern
- 基于Spring解决jsp传值乱码问题 get post
- Spring mvc redirect跳转路径问题
- 关于Spring MVC redirect传值问题
- 解决Spring Web MVC中POST中文乱码问题
- Spring MVC POST请求转到GET
- Spring MVC Flash Attribute 的讲解与使用示例
- Spring MVC Flash Attribute 的讲解与使用示例
- Spring MVC Flash Attribute 的讲解与使用示例
- Spring MVC使用Flash Attribute展现提示消息
- Spring MVC Tomcat下GET请求和POST请求乱码的解决
- Spring MVC redirect
- spring mvc 解决 问题
- vncserver:command not found
- 陈宁宁的博客开通啦,请各位不吝赐教
- 欢迎使用CSDN-markdown编辑器
- GSM-MODEM调试备忘(一)
- 《你早该这么玩excel》大表哥伍昊新课开讲——顶你学堂独家
- Spring MVC Flash Attribute 解决POST/Redirect/GET模式问题缺陷
- 【SDOI 2015】【BZOJ 3994】约数个数和
- iOS开发C语言字符串
- 将一个数组快速反序排列
- hdoj 1754 I Hate It【线段树】
- android中实现如10000000转为10,000,000.00这样的数字格式
- android应用开机自启动及失败原因(BOOT_COMPLETED应用)
- requestFeature() must be called before adding content错误处理
- android 判断字符串是否同时包含小写字母与数字