在struts1.x中防止重复提交的实现
当我们在Struts中提交表单后,可以通过会退按钮返回到原来的表单页面,进行重复性提交。这在很多情况下是不允许的。比如当注册用户的表单中,当提交成功后,回退或者按刷新按钮可以再次提交表单,这样造成数据的不一致性,因为数据已经提交。
Struts中可以利用同步令牌机制来解决重复提交表单的问题。在Struts的Action中提供了和同步令牌相关的方法
protected boolean isTokenValid(HttpServletRequest request)
此方法用来判断当前用户会话中存储的令牌值和当前请求参数的令牌值是否一致,如果不一致返回false,一致则返回true。当我们利用Action进行相应调用处理时,就是利用此方法判断令牌值是否一致来决定是否进行处理
protected void saveToken(HttpServletRequest request)
此方法创建一个新的令牌值,将其保存在当前的会话(session)范围内。如果当前的会话对象不存在,则创建会话对象
protected void resetToken(HttpServletRequest request)
此方法对当前会话范围的令牌值进行复位操作,即删除当前会话范围的令牌值
了解了以上方法后,我们就可以在我们的应用中利用这些方法防止表单的重复性提交。例如在此我们实现一个简单的数据增加操作,首先需要显示给用户一个信息录入表单,显示此录入表单页面前,需要给当前的请求分配一个同步令牌值,并存储在当前的会话范围内。并在当前的录入页面中生成一个隐藏域,令牌值作为此隐藏域的内容,当用户提交时,控制器获取隐藏域的内容,利用isValidToken方法判断此参数内容是否和会话中存储的令牌值内容匹配,如果不匹配则进行适当的错误处理
由于要在页面显示前生成此次请求的令牌值,所以通过一个额外的Action生成令牌值,并转发到录入页面。
下面是我做的一个例子:
配置文件struts-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN" "http://struts.apache.org/dtds/struts-config_1_3.dtd">
<struts-config>
<form-beans >
<form-bean name="registerForm" type="com.struts.token.form.RegisterForm" />
</form-beans>
<global-exceptions />
<global-forwards />
<action-mappings >
<action
attribute="registerForm"
input="/register.jsp"
name="registerForm"
path="/register"
scope="request"
parameter="action"
type="com.struts.token.action.RegisterAction"
cancellable="true">
<forward name="succ" path="/registerSucc.jsp" />
<forward name="fail" path="/registerFail.jsp" />
</action>
<action
path="/produce"
scope="request"
type="com.struts.token.action.ProduceToken">
<forward name="succ" path="/register.jsp" />
</action>
</action-mappings>
<message-resources parameter="com.struts.token.ApplicationResources" />
</struts-config>
自动生成令牌机制的action:
package com.struts.token.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import com.struts.token.form.RegisterForm;
//作者,朱湘鄂
//完成日期:10-05-21
//目的:在用户注册之前生成一个令牌值
public class ProduceToken extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
RegisterForm registerForm = (RegisterForm) form;// TODO Auto-generated method stub
//Save a new transaction token in the user's current session,
// creating a new session if necessary.
this.saveToken(request);//生成令牌值,并且存入到当前的会话中
return mapping.findForward("succ");//转入到注册页面
}
}
将令牌机制的值与请求的值相比较的action:
/*
* Generated by MyEclipse Struts
* Template path: templates/java/JavaClass.vtl
*/
package com.struts.token.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.actions.DispatchAction;
import com.struts.token.form.RegisterForm;
/**
* MyEclipse Struts//作者:朱湘鄂
* Creation date: 05-21-2010
*
* XDoclet definition:这个主要是一个处理业务逻辑的action,
* 用来将当前会话中的令牌机制值和请求中的对比
* @struts.action path="/register" name="registerForm" input="/register.jsp" scope="request" validate="true"
* @struts.action-forward name="succ" path="/rigisterSucc"
* @struts.action-forward name="fail" path="/registerFail.jsp"
*/
public class RegisterAction extends DispatchAction {
public ActionForward add(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
//struts机制,将注册的信息封装到了一个formBean中了
RegisterForm registerForm = (RegisterForm) form;// TODO Auto-generated method stub
String username = registerForm.getUsername();//得到用户注册的姓名值
String password = registerForm.getPassword();//得到用户注册的密码值
//这里可以对数据进行一些操作,由于主要功能是进行令牌机制的验证,所以省了...、
/*
* 判断会话中的令牌机制值域与
如果不匹配则重新生成令牌值
转发到fail(即信息录入页面重新录入)。
如果匹配则删除当前会话中的令牌值(这样,
当点回退或者刷新按钮重复提交时,因为此时令牌值已经被删除,
显然不匹配,则不进行实际的业务操作,即防止了重复提交),
完成实际的业务操作。
*/
//这里是不匹配
if(!this.isTokenValid(request)){
//再创建一个,存入到会话中
this.saveToken(request);//生成令牌机制
return mapping.findForward("fail");
//这里是匹配
}else{
this.resetToken(request);//删除令牌机
//制值
return mapping.findForward("succ");
}
}
}
注册的页面:
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html"%>
<html>
<head>
<title>JSP for RegisterForm form</title>
</head>
<body>
<html:form action="/register?action=add">
username : <html:text property="username"/><html:errors property="username"/><br/>
password : <html:password property="password"/><html:errors property="password"/><br/>
<html:submit/><html:cancel/>
</html:form>
</body>
</html>
直接访问那个生成令牌机制的action,然后,跳转到注册的页面,然后,根据跳转页面的action,来显示相应的页面!!!
over了!