前幾天進行了對現時平臺(基於struts2)的一些細節改進思考,總結了一些方法和技巧,貼出來共享一下吧。
防止表單重複提交
問題描述:用戶提交表單時可能因爲網速的原因,或者網頁被惡意刷新,致使同一條記錄重複插入到數據庫中,這是一個比較棘手的問題。我們可以從客戶端和服務器端一起着手,設法避免同一表單的重複提交。
解決方案:
1.使用客戶端腳本
提到客戶端腳本,經常使用的是JavaScript進行常規輸入驗證。在下面的例子中,我們使用它處理表單的重複提交問題,請看下面的代碼:
<form method="post" name="register" action="test.php" enctype= |
上述方法,主要是將按鈕的提交狀態改變爲disabled。
2.利用Struts2的令牌機制。
防止表單重複提交主要用的到標籤是<s: token />,攔截器 <interceptor-ref name="token" />,還有一個默認的返回值<result name="invalid.token">/input.jsp</result>
在頁面加載時,<s: token />產生一個GUID(Globally Unique Identifier,全局唯一標識符)值的隱藏輸入框如:
<input type="hidden" name="struts.token.name" value="struts.token"/>
<input type="hidden" name="struts.token" value="BXPNNDG6BB11ZXHPI4E106CZ5K7VNMHR"/>
同時,將GUID放到會話(session)中;在執行action之前,“token”攔截器將會話token與請求token比較,如果兩者相同,則將會話中的token刪除並往下執行,否則向actionErrors加入錯誤信息。如此一來,如果用戶通過某種手段提交了兩次相同的請求,兩個token就會不同。
下面用零配置來演示 token的作用
/WEB-INF/content/test-success.jsp
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
</head>
<body>
<s:actionerror/>
<s:form action="test!save.action" method="POST">
<s:textfield name="message" label="請輸入您的信息"/>
<s:token name="token"/>
<s:submit value="確定" />
</s:form>
</body>
</html>
/WEB-INF/content/error.jsp
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<body>
<s:actionerror/>
不能重複提交表單!
</body>
</html>
/WEB-INF/content/test-ok.jsp
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<body>
SAVE OK!
</body>
</html>
TestAction.java
package com.fun.actions;
import org.apache.struts2.convention.annotation.InterceptorRef;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import com.opensymphony.xwork2.ActionSupport;
@Results({
@Result(name="invalid.token",location="/index.html",type="redirect")})
@InterceptorRefs({@InterceptorRef(value="token",params={"includeMethods","save"}),@InterceptorRef("defaultStack") })
public class TestAction extends ActionSupport {
private String message;
public String execute(){
return SUCCESS;
}
public String save(){
return "ok";
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
其實,最主要的就是 token攔截器中還有 includeMethods()這個方法,表示的是,Action中的哪個方法需要經過攔截器。屬性excludeMethods()這個方法表示的是哪個方法不經過攔截器。如果,沒有加上“defaultStack”這個攔截器的話,則ActionContext的值將爲null。