- 数据重复提交问题
如注册时的表单提交,用户在浏览器点击提交按钮后提交数据,到达sbt.action中。sbt.action完成数据插入后,跳转到注册成功的提示页(转发)。此时客户端浏览器刷新页面,则会重复提交数据。
- 页面重定向
如注册regist.do完成数据提交之后,通过重定向到registFish.do后展示注册成功页面。此时浏览器的地址已变为registFish.do,刷新后不会重复提交数据。参数传递可使用RedirectAttribute进行传递。
- 提交的数据在数据库添加唯一性约束。
注册的users,其loginName在数据库添加唯一性约束
- Ssm架构中自定义拦截器以及session来处理
- 通过继承HandlerInterceptorAdapter来实现一个自定义拦截器,通过实现preHandle()方法,在其中实现拦截功能。需添加配置<mvc:interceptors>。
- 通过request.getParameterMap()获取对应的请求参数,以及request.getRequestURI()来获取请求地址;将其组合为一个String并存储在session中。
- 当接收到特定请求时,通过B中的操作生成一个请求url-请求参数的字符串,该字符串和session中的对应字符串进行equals()比较。如果相等则拦截器preHandle()返回false。
源码详见
- UUID和session来处理
如访问注册表单页之前,产生UUID并写入session中;在表单页中通过隐藏域的方式提交该UUID。在提交的action中,判断该UUID和session中的是否一致,一致则提交,不一致则证明是表单的重复提交。在表单提交成功时,移除session中的对应的UUID
1.springmvc拦截器配置
<!-- Spring拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/*.html" />
<mvc:mapping path="/*.do" />
<bean class="com.wpao.shop.util.SameUrlDataInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
2.SameUrlDataInterceptor
package com.wpao.shop.util;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.log4j.Logger;
import org.json.JSONObject;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
/**
* 一个用户 相同url 同时提交 相同数据 验证;
* 主要通过 session中保存到的url 和 请求参数;如果和上次相同,则是重复提交表单;主要针对注册表单提交;
* @jason,71202;
*/
public class SameUrlDataInterceptor extends HandlerInterceptorAdapter {
Logger logger = Logger.getLogger(this.getClass());
@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 annotation = method.getAnnotation(SameUrlData.class);
if (annotation != null) {
if(repeatDataValidator(request)) { //如果数据重复相同:
logger.info("***[theuser]拦截器,数据提交重复,请求URL:"+request.getRequestURI());
response.sendRedirect("toregist.html");
return false;
} else {
return true;
}
}
return true;
} else {
return super.preHandle(request, response, handler);
}
}
/**
* 验证同一个url数据是否相同提交,相同返回true
* @param httpServletRequest
*/
public boolean repeatDataValidator(HttpServletRequest request)
{
JSONObject jsonObject = new JSONObject(request.getParameterMap());
String params = jsonObject.toString(); //JsonMapper.toJsonString(request.getParameterMap());
String url = request.getRequestURI();
Map<String,String> map=new HashMap<String,String>();
map.put(url, params);
String nowUrlParams = map.toString();
logger.info("***[theuser]拦截器,表单提交Data:"+nowUrlParams);
HttpSession session = request.getSession();
Object preUrlParams = session.getAttribute("repeatData");
if(preUrlParams == null) { //如果上一个数据为null,表示还没有访问页面:
session.setAttribute("repeatData", nowUrlParams);
return false;
}else { //否则,已经访问过页面:
if(preUrlParams.toString().equals(nowUrlParams)) { //如果上次url+数据和本次url+数据相同,则表示重复添加数据:
return true;
}else { //如果上次url+数据 和本次url加数据不同,则不是重复提交:
session.setAttribute("repeatData", nowUrlParams);
return false;
}
}
}
}
3.注解定义
package com.wpao.shop.util;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SameUrlData {
}
4.controller中注解配置
//注册提交:
@RequestMapping("/regist")
@SameUrlData
public String regist(HttpServletRequest request,Model model,User user) {
String failed = "register/register";
String reqmethod = request.getMethod();
}
5.日志输出
14:43:04,267 INFO SameUrlDataInterceptor:56 - ***[theuser]拦截器,表单提交Data:{/wpao-theuser/regist.html={"uyzcode":["vjc9ci"],"rePwd":["15510856100"],"yxcheckbox":["on"],"uqq":["15510856100"],"umobile":["15510856810"],"yxcheckboxhidden":["ok"],"dxmessage":["927792"],"loginPwd":["15510856100"],"loginName":["15510856100"]}}
14:43:04,738 INFO RegisterController:313 - ***[theuser]会员注册提交,账户名[15510856100],返回结果[ok].
14:43:04,740 INFO RegisterController:344 - ***[theuser]会员注册提交,账户名[15510856100],手机号[15510856820],注册成功!生成ID[237761].
14:43:06,932 INFO SameUrlDataInterceptor:56 - ***[theuser]拦截器,表单提交Data:{/wpao-theuser/regist.html={"uyzcode":["vjc9ci"],"rePwd":["15510856100"],"yxcheckbox":["on"],"uqq":["15510856100"],"umobile":["15510856810"],"yxcheckboxhidden":["ok"],"dxmessage":["927792"],"loginPwd":["15510856100"],"loginName":["15510856100"]}}
14:43:06,934 INFO SameUrlDataInterceptor:31 - ***[theuser]拦截器,数据提交重复,请求URL:/wpao-theuser/regist.html