方法
Struts2只需在<s:form/>
標籤範圍中添加<s:token/>
標籤,即可實現防止表單重複提交
原理
假設頁面添加的是<s:token name="hello"/>
,運行項目後查看此頁面源文件,會發現它被轉化成了如下代碼
<!--
若頁面中寫的是<s:token/>,那麼被生成的代碼是像下面這樣
<input type="hidden" name="struts.token.name" value="struts.token"/>
<input type="hidden" name="struts.token" value="KF98D738ZRY8TNVQWJL3GYSB8LO5U748"/>
-->
<input type="hidden" name="struts.token.name" value="hello"/>
<input type="hidden" name="hello" value="HUZU1PLOZ2111AAN5XN48I5914IUM668"/>
由於<s:token/>
是個標籤,所以必然存在相應的標籤庫的代碼文件
當服務器解析到<s:token/>
時,該標籤對應的類文件就會自動生成兩個hidden
其中struts.token.name
是永遠固定的,第一個hidden的值與第二個hidden的name是關聯在一起的
第二個hidden的值是Struts2產生的一個32位的GUID(Globally
Unique Identifier全局唯一標識符)
然後Struts2會這樣:HttpSession.setAttribute("hello",
"HUZU1PLOZ2111AAN5XN48I5914IUM668")
提交表單時,名爲token
的表單重複提交的攔截器發現了hidder,於是會去嘗試截獲struts.token.name
截獲到則讀取該hidden的值hello,接着拿hello去讀另一個hidden的值以及HttpSession裏面的值
若讀到的值相同,則認爲表單第一次提交,於是HttpSession.removeAttribute("hello")
,接着程序正常執行
若第一次提交後,再刷新頁面或第二次提交,這時的GUID再與HttpSession比較,倆值不同於是認爲第二次提交
-
表單重複提交攔截器
這裏用到的token攔截器,對應
org.apache.struts2.interceptor.TokenInterceptor
,它繼承了MethodFilterInterceptor
攔截器。另外token
並沒有配置在defaultStack
攔截器裏。我們可以發現Struts2採用的防止表單重複提交的方式與Struts1是一模一樣的,原理上沒有任何改變。但在使用方式上卻簡化又簡化,不像Struts1那樣還要寫代碼,顯然比Struts1採用的token方式簡潔得多得多 -
重複提交表單時的提示信息的國際化
表單重複提交時的報錯信息是放在ActionError中的,在前臺頁面對錯誤信息進行輸出結果如下所示
The form has already been processed or no token was supplied, please try again.
然後在org.apache.struts2.struts-message.properties中找到該信息對應的key,就可以設置國際化信息以輸出中文提示了。 -
tokenSession攔截器
1、使用token攔截器的做法在論壇用得比較多,不過也有些商務網站使用的是tokenSession攔截器
2、商務網站通常不管用戶刷新多少次頁面,都一直顯示提交成功頁面給用戶
只不過這個提交成功可能會讓用戶誤解。事實上也僅僅提交成功了一次,後面的都被忽略了
這個功能就是由tokenSession攔截器實現的,它不需配置任何result
,返回的永遠是成功頁面
3、即使用tokenSession攔截器時,前臺不用做任何改變,還是在表單裏面放一個<s:token/>
然後在struts.xml
中寫成<interceptor-ref name="tokenSession"/>
即可
4、當發現用戶重複提交時,由於tokenSession攔截器的作用,它會返回result爲success
的頁面
我們也可以在Action的方法中打印輸出一句話來測試是否僅僅第一次的提交被成功執行了
經過測試得知,無論用戶在客戶端刷新幾百遍幾千遍,最後只有第一次的提交操作被執行了
示例
下面展示一個完整的代碼
這裏用的是Struts2.1.8.1
,首先是web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>testToken.jsp</welcome-file>
</welcome-file-list>
</web-app>
下面是用於輸入用戶名和密碼以測試重複提交表單的testToken.jsp頁面
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<h2>測試表單重複提交</h2>
<s:actionerror/>
<s:form action="testToken" theme="simple">
<s:token name="hello"/>
姓名:<s:textfield name="username"/><br/>
密碼:<s:password name="password"/><br/>
<s:submit value="提交"/>
</s:form>
用戶名和密碼正確時顯示的result.jsp頁面
<%@ page pageEncoding="UTF-8"%>
<h2>Login Success</h2>
下面是Struts2的配置文件struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1//EN"
"http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<package name="struts2.1" extends="struts-default">
<!-- 當Struts2發現用戶進行表單重複提交時,它會尋找invalid.token -->
<!-- 並根據invalid.token找到與之對應的頁面。所以記得要配置invalid.token結果 -->
<action name="testToken" class="com.jadyer.action.LoginAction" method="testToken">
<result>/result.jsp</result>
<result name="invalid.token">/testToken.jsp</result>
<interceptor-ref name="token"/>
<interceptor-ref name="defaultStack"/>
</action>
</package>
</struts>
最後是處理所輸入的用戶名和密碼的LoginAction.java
package com.jadyer.action;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport {
public String testToken() throws Exception {
return SUCCESS;
}
}