Struts2筆記之表單重複提交
前言
防止表單重複提交在web開發中是一個經常遇到的問題,一般來避免重複提交有兩種方式:客戶端JavaScript程式碼實現和服務端程式碼實現。這裡主要介紹服務端的實現方式。在服務端實現表單重複提交的基本原理是:通過建立一個Session物件,併產生一個令牌值,將這個令牌值作為隱藏域隨表單一起傳送給客戶端,同時在Session中儲存令牌值。在使用者提交表單的時候判斷提交引數的令牌值與Session中的是否相等,如果相等則清除,不再使用這個令牌值,,然後執行後續的處理;如果兩者不相等,表示已經提交過表單,服務端產生一個新的令牌值並儲存到Session中。當用戶下次訪問的的時候,將新產生的領牌值傳送到客戶端。
什麼是表單的重複提交?
> 在不重新整理表單頁面的前提下: >> 多次點選提交按鈕 >> 已經提交成功, 按 "回退" 之後, 再點選 "提交按鈕". >> 在控制器響應頁面的形式為轉發情況下,若已經提交成功, 然後點選 "重新整理(F5)" > 注意: >> 若重新整理表單頁面, 再提交表單不算重複提交 >> 若使用的是 redirect 的響應型別, 已經提交成功後, 再點選 "重新整理", 不是表單的重複提交
Struts2的實現方式
在Struts2中通過使用攔截器來實現的,機制與前言中採用令牌的方式是一樣的。可以通過兩種方式實現避免重複表單(實際上就是兩個不同的攔截器):token攔截器和tokenSession攔截器。由於在struts-default.xml的預設攔截器棧中並沒有將這兩個攔截器作為預設實現,所以需要在action中手動新增這兩個攔截器。這兩種方式的區別在於:使用token攔截器重複提交表單的時候,瀏覽器會跳轉到一個錯誤頁面,而使用更tokenSession攔截器重複提交表單的話是不會跳轉的,仍然在成功之後頁面。需要注意的是,使用者兩個攔截器重複提交表單的時候,都只會向伺服器提交一次請求,所以這種方式可以有效降低伺服器的負擔。
具體的例子
在使用以上攔截器進行測試的時候,需要如下步驟: 步驟一:編寫login.jsp、success.jsp和error.jsp三個頁面 login.jsp
...
<s:form action="tokenWait" namespace="/" method="post">
<s:textfield label="使用者名稱" name="user.username"></s:textfield>
<s:password label="密碼" name="user.password"></s:password>
<!-- 這個標籤不能少 -->
<%-- <s:token></s:token> --%>
<s:submit value="登入"></s:submit>
</s:form>
...
success.jsp
...
<s:property value="user.username"/>,<%=new Date() %>
...
error.jsp
<html>
<body>
<!-- 登入失敗,請重新登入
<a href="login.jsp">返回</a> -->
您已經提交過表單了!
</body>
</html>
步驟二:編寫action
package action;
import java.util.ArrayList;
import java.util.List;
import bean.User;
import com.opensymphony.xwork2.ActionSupport;
public class TokenAction extends ActionSupport {
private static final long serialVersionUID = 1L;
private User user;
@Override
public String execute() throws Exception {
Thread.sleep(3000);
List<User> users = new ArrayList<User>();
users.add(user);
for (User user : users) {
System.out.println(user);
}
return SUCCESS;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
步驟三:配置struts.xml
<!-- 避免表單重複提交 -->
<action name="token" class="action.TokenAction">
<!-- 配置Token攔截器 -->
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="token"></interceptor-ref>
<!-- 如果重複提交,則跳轉到error.jsp -->
<result name="invalid.token">/error.jsp</result>
<result>/success.jsp</result>
</action>
<action name="tokenSession" class="action.TokenAction">
<!-- 配置TokenSession攔截器 -->
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="tokenSession"></interceptor-ref>
<!-- 如果重複提交,則跳轉到error.jsp -->
<result name="invalid.token">/error.jsp</result>
<result>/success.jsp</result>
</action>
這裡需要提出的是,action中name屬性為invalid.token
的result是不可少的。
步驟四:釋出測試 經過測試,發現使用token攔截器在重複提交表單的時候會轉到error.jsp,而使用tokenSession攔截器在重複提交表單的時候不會轉到error.jsp。
顯示等待頁面
有時候在action需要處理較長時間的時候,一般是5到10分鐘,在這種情況下向使用者顯示一個等待頁面可能會比較友好一些。在Struts2中通過使用execAndWait攔截器就可以非常輕鬆實現這點。
execAndWait的工作機制:
execAndWait攔截器能夠讓一個A執行時間超過5分鐘的Action在後臺執行,並向用戶顯示一個等待頁面。之所以是5分鐘是因為這樣防止HTTP請求超時。當一個請求到來的時候,execAndWait攔截器會建立一個執行緒來執行Session,然後返回一個等待頁面,這樣使用者就知道請求在處理中。等待頁面包含了自動重新整理功能,在超時之前,瀏覽器會向初始請求的action再次發起請求,以便知道後臺action是否已經執行完畢。如果action仍然沒有執行完畢,則繼續顯示等待頁面,如果action已經執行完畢,則等待頁面將發生跳轉,向用戶處理結束之後的頁面。
execAndWait攔截器有以下幾個引數:
- threadPriority:執行執行緒的優先順序
- delay:指定在顯示等待頁面前初始的延遲載入時間,單位是毫秒
- delaySleepInternal:指定檢查後臺執行緒是否執行完畢的時間間隔,必須和delay引數一起使用,單位是毫秒,預設是100毫秒。表示每100毫秒進行一次檢查
使用execAndWait攔截器顯示等待頁面,首先需要編寫一個等待頁面:
...
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>等待頁面</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="refresh" content="3;url=tokenWait.action">
</head>
<body>
您的請求正在處理,請稍等。
<span id="time" style="font-size:30px;color:red;font-face:隸書"></span>秒後頁面將自動跳轉
<script type="text/javascript">
var start = 3;
var step = -1;
function timer () {
document.getElementById("time").innerHTML = start;
if(start > 0){
start = start + step;
}
setTimeout("timer()",1000);
}
window.onload = timer;
</script>
</body>
</html>
在head 標籤中需要新增自動重新整理meta標籤,不然是不會出發自動檢查的。在這個等待頁面中,表示3秒後就會跳轉到成功頁面。
之後是新增execAndWait攔截器的配置:
<!-- 顯示自動等待頁面 -->
<action name="tokenWait" class="action.TokenAction">
<result name="wait">/wait.jsp</result>
<result>/success.jsp</result>
<interceptor-ref name="defaultStack">
<!-- 把default方法排序在外,表示不攔截!default.action -->
<param name="excludeMethods">default</param>
</interceptor-ref>
<interceptor-ref name="execAndWait">
<!-- 把default方法排序在外,表示不攔截!default.action -->
<param name="excludeMethods">default</param>
<!-- 等待延遲時間 -->
<param name="delay">1000</param>
</interceptor-ref>
</action>
注意到TokenAction類中,使用Tread.sleep(3000),表示通過讓執行緒休眠的方式延長action的處理時間,還有一點要注意的是struts.xml中execAndWait攔截器的delay引數的值需要小於Thread.sleep(time)的時間。這樣就能保證在action處理結束之前完成顯示等待頁面,不然很可能會直接success.jsp頁面了。
--------------------- 本文來自 rhwayfunn 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/u011116672/article/details/50390214?utm_source=copy