适用场景:在从页面向数据库中插入数据时,比如用户注册,提交留言等,当操作成功之后,再点击“后退”按钮,退回到表单页再次提交表
单,如果对此重复提交未加处理,将会再次提交成功,数据库中就会有重复的数据,这种情况是不合理的。
Struts解决方案:Struts框架的令牌机制(TOKEN)很好的解决了这个表单重复提交的问题。
基本原理:服务器端在处理到达的请求之前,会将请求中包含的令牌值与保存在当前用户会话中的令牌值进行比较,看是否匹配。在处理完该请求后,且在答复发送给客户端之前,将会产生一个新的令牌,该令牌除传给客户端以外,也会将用户会话中保存的旧的令牌进行替换。这样如果用户回退到刚才的提交页面并再次提交的话,客户端传过来的令牌就和服务器端的令牌不一致,从而有效地防止了重复提交的发生。
下面通过一个例子来说明:
例子操作内容:通过留言板向数据库中插入一条留言记录。
文件包括:
messageAdd.jsp
messageSubmit.jsp
messageError.jsp
messageSuccess.jsp
messageAction.java
messageForm.java
struts-config.xml
messageAdd.jsp
<%@ page language="java" pageEncoding="gbk"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<%@include file="/header.jsp" %>
<title>防止重复提交</title>
</head>
<body>
<a href="<%=basePath%>strutsTest.do?method=add">我要留言</a>
</body>
</html>
messageSubmit.jsp
<%@ page language="java" pageEncoding="gbk"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<%@include file="/header.jsp"%>
<title>add.jsp</title>
</head>
<body>
<html:form. action="strutsTest.do?method=insert" method="post" >
<table>
<tr>
<td>
留言
</td>
</tr>
<tr>
<td>
<textarea id="content" name="content"></textarea>
</td>
</tr>
<tr>
<td>
<input type="submit" value="提交"/>
<input type="reset" value="重置"/>
</td>
</tr>
</table>
</html:form>
</body>
</html>
messageError.jsp
<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>错误页面</title>
</head>
<body>
请不要重复提交
</body>
</html>
messageSuccess.jsp
<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>成功页面</title>
</head>
<body>
留言成功
</body>
</html>
MessageAction.java
public class MessageAction extends DispatchAction
{
public ActionForward add(ActionMapping mapping, ActionForm. form, HttpServletRequest request, HttpServletResponseresponse)
{
TokenProcessor processor=TokenProcessor.getInstance();
processor.saveToken(request);
return mapping.findForward("add");
}
public ActionForward insert(ActionMapping mapping, ActionForm. form, HttpServletRequest request, HttpServletResponseresponse)
{
MessageForm.strutsForm=(MessageForm)form;
String message=strutsForm.getContent();
TokenProcessor processor=TokenProcessor.getInstance();
String tour1="error";
if(message!=null&&!"".equals(message))
{
if(processor.isTokenValid(request,true))
{
tour1="success";
}
else
{
processor.saveToken(request);
}
}
return mapping.findForward(tour1);
}
}
MessageForm.java
public class MessageForm. extends ActionForm
{
private static final long serialVersionUID =1L;
private String content="";
public String getContent()
{
return content;
}
public void setContent(String content)
{
this.content = content;
}
}
struts-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN""http://struts.apache.org/dtds/struts-config_1_2.dtd">
<struts-config>
<data-sources />
<form-beans >
<form-bean name="messForm"
type="com.xinglongjian.struts.action.MessageForm" />
</form-beans>
<global-exceptions />
<action-mappings>
<action path="/strutsTest" parameter="method" name="messForm"
type="com.xinglongjian.struts.action.MessageAction" >
<forward name="add" path="/struts/messageSubmit.jsp"></forward>
<forward name="error" path="/struts/messageError.jsp"></forward>
<forward name="success" path="/struts/messageSuccess.jsp"></forward>
</action>
</action-mappings>
</struts-config>
注意事项:
1 TokenProcessor类,这个类是对Token进行操作的。可以查看源代码弄明白saveToken(request),isTokenValid(request,true)等方法的具体实现。
从代码中可以看出,token值是由用户的Session ID和当前时间算出来的,并将其放入session中,key为Globals.TRANSACTION_TOKEN_KEY
2,页面中form标签要用struts的标签<html:form>原因是:因为当应用服务器初始化填写表单页面遇到标签<html:form>时,便会调用struts的FormTag类的renderToken()方法。该方法会当检测到session中的Globals.TRANSACTION_TOKEN_KEY不为空时,在填写表单页面创建元素:
<input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="">,
名称为:org.apache.struts.taglib.html.TOKEN就是Constants.TOKEN_KEY;
值为:session中的Globals.TRANSACTION_TOKEN_KEY的值.
另外必须在struts-config.xml中配置form-bean,而且name属性值要与action的name属性值一致,因为struts的form标签与一个Form对象对应。
<!----------------做个小记录...-------------->