1. 程式人生 > >Struts2筆記之表單重複提交

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