JFinal避免表单重复提交
前言
流程思路
(同Spring MVC一样)
sessionId
:是Request.getSession().getId() 获取session里面的id
url
:是 Request.getRequestURI() 获取的网址的url
params
:是Request.getParameterMap()转的json字符串再转的String
表单第一次提交数据:
进入拦截器,从redis 缓存中获取key(sessionId+url)为空,会把sessionId+url为key,url+param为value存入redis缓存,返回为true,进入对应 Action
表单第二次提交数据:
进入拦截器,从redis 缓存中获取key(sessionId+url)不为空,然后拿从缓存中获取的值和上一个进行判断,如果相同则为表单重复提交,返回false;如果不相同,返回为true,进入对应 Action
涉及环境
Jfinal 框架
Tomcat (暂无限制版本) 或 jFinal 的核心服务器
Redis 缓存
jFinal避免表单重复提交配置
表单重复提交拦截器配置
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.core.Controller;
import net.sf.json.JSONObject;
/**
* @author 马家立
* @version 创建时间:2018年12月27日下午5:01:37
* @Description: 提交时校验页面中url和提交的参数数据并与redis缓存中的url校验,一致通过,时间过期无效
*/
public class RepeatSaveInterceptor implements Interceptor {
private static Logger logger = Logger.getLogger("repeatInterceptor");
//redis 缓存的存储过期时间(单位/s)
static int overTime = 60;
/**
* 表单重复提交拦截器
*/
@Override
public void intercept(Invocation inv) {
// Auto-generated method stub
logger.info("进入重复提交表单拦截器:RepeatSaveInterceptor的intercept");
Controller controller= inv.getController();
//验证同一个url数据是否相同提交 ,相同返回false
boolean token = repeatDataValidator(controller.getRequest());
if(token){
inv.invoke(); // 继续执行action中的方法
}
}
/**
* @Title:repeatDataValidator
* @author:马家立
* @date:2018年12月28日 上午10:37:35
* @Description: 验证同一个url数据是否相同提交 ,相同返回false
* @param:@param httpServletRequest
* @param:@return true:url或者提交的参数数据不同;false:url相同,提交的参数数据相同
* @return:boolean
*/
public boolean repeatDataValidator(HttpServletRequest httpServletRequest) {
logger.info("进入RepeatSaveInterceptor的repeatDataValidator");
try {
RedisUtil redis = new RedisUtil();
String sessionId = httpServletRequest.getSession().getId();
logger.info("获得sessionId:" + sessionId);
//获取参数的map,然后转为json字符串
Map<String, String[]> mapa = httpServletRequest.getParameterMap();
JSONObject jsonObject = JSONObject.fromObject(mapa);
logger.info("jsonObject的输出结果是:" + jsonObject);
//将json对象转化为json字符串
String params = jsonObject.toString();
String url = httpServletRequest.getRequestURI();
logger.info("url是:" + url);
//创建一个新的map用于存储新的url和提交的参数数据,用于和session里面的相比较
Map<String, String> map = new HashMap<String, String>();
map.put(url, params);
String nowUrlParams = map.toString();
Object preUrlParams = redis.get(sessionId + url);
logger.info("表单拦截器配置成功");
// 如果上一个数据为null,表示还没有访问页面
if (preUrlParams == null) {
redis.setex(sessionId + url, overTime, nowUrlParams);
return true;
} else {// 否则,已经访问过页面
// 如果上次url+数据和本次url+数据相同,则表示重复添加数据
if (preUrlParams.toString().equals(nowUrlParams)) {
return false;
} else { //如果上次 url+数据 和本次url加数据不同,则不是重复提交
redis.setex(sessionId + url, overTime, nowUrlParams);
return true;
}
}
} catch (Exception e) {
logger.error("进入SameUrlDataInterceptor的repeatDataValidator");
e.printStackTrace();
return false;
}
}
}
表单重复提交日志配置
#避免表单重复提交
log4j.logger.repeatInterceptor = info,repeatInterceptor
log4j.appender.repeatInterceptor=org.apache.log4j.RollingFileAppender
log4j.appender.repeatInterceptor.File=D:\\ProjectLog\\OASystem\\repeatInterceptor.log
log4j.appender.repeatInterceptor.MaxFileSize=200MB
log4j.appender.repeatInterceptor.MaxBackupIndex=10
log4j.appender.repeatInterceptor.layout=org.apache.log4j.PatternLayout
log4j.appender.repeatInterceptor.layout.ConversionPattern=%-d{yyyy-MM-dd HH\:mm\:ss,SSS}[%p] [%l][%t]%n \u3010%m\u3011%n%n
log4j.additivity.repeatInterceptor=false
Action或 Controller 注解方式使用
在需要进行重复表单验证的方法上面加上@Before(RepeatSaveInterceptor.java)
注解即可,这个注解即是第一步写的自定义注解
验证是否配置成功
查看log4j的配置路径下是否生成 repeatInterceptor.log 文件
关于注解
java中元注解有四个: @Retention @Target @Document @Inherited;
@Retention:注解的保留位置
@Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS) // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Target:注解的作用目标
@Target(ElementType.TYPE) //接口、类、枚举、注解
@Target(ElementType.FIELD) //字段、枚举的常量
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法参数
@Target(ElementType.CONSTRUCTOR) //构造函数
@Target(ElementType.LOCAL_VARIABLE)//局部变量
@Target(ElementType.ANNOTATION_TYPE)//注解
@Target(ElementType.PACKAGE) ///包
@Document:说明该注解将被包含在javadoc中
@Inherited:说明子类可以继承父类中的该注解