天天看點

(轉)Struts2.0 避免重複送出

以下内容轉貼于 http://hi.baidu.com/menglinxi_a/blog/item/8bdc6ade9579325295ee37ee.html Struts2.0 避免重複送出 2008-09-30 00:53

有些時候一些操作會非常的耗費時間(Long Lived Operation),例如這個資料庫的導出,表表生成等。有些時候程式的使用者看到很長時間伺服器沒有反應,傾向于多次點選送出按鈕。這樣恰恰相反,由于重新使伺服器運作相同的長時間操作,反而讓反應時間更慢。

我們如何來制禦這樣的操作呢?

我們能不能在每個頁面生成的時候,自動生成一個特殊的隐藏字段,這個隐藏字段具有唯一性。每次向伺服器送出請求的時候,伺服器就記錄下這個隐藏字段,當在看到有相同的特殊字段的内容送出時,伺服器認為這是重複送出,将畫面定位到一個特殊的畫面來提示客戶重複送出了資料。

在Struts2中,内容了對輕按兩下的制禦操作,特殊的隐藏字段叫做token。

如何在生成頁面的時候生成token字段?

使用token标簽。

代碼如下:

<s:url id="formUrl" action="longLivedTokenAction"/>

<s:form action="%{formUrl}" method="post">

<s:token />

<s:textfield name="name" label="Name"/>

<s:textfield name="password" label="Password"/>

<s:submit/>

</s:form>

其中<s:token />标簽用來生成一個唯一的隐藏字段,在運作的時候生成的内容如下:

<input type="hidden" name="struts.token" value="C21ZWHEH0Q4B6FY15ZO5BFM1I9W8SIQH" />

如何在伺服器上記錄已經送出的token?

可以使用token intercepter。

代碼如下:

<action name="longLivedTokenAction" class="com.jpleasure.LongLivedTokenAction">

<interceptor-ref name="token"/>

<interceptor-ref name="basicStack"/>

<result>/jsp/longLived.jsp</result>

<result name="invalid.token">/jsp/invalidToken.jsp</result>

</action>

上述<interceptor-ref name="token"/>表示所有的請求必須經過token Interceptor,token Interceptor作用就是紀錄所有已經送出的token。那麼發現送出的token被重複送出的時候怎麼辦呢?也許大家已經猜到了,他會重定向到invalid.token所指向的頁面,也就是/jsp/invalidToken.jsp。

基于Interceptor的執行順序按照struts.xml中定義的順序,為了更早的結束重複送出的處理,應該将Token Interceptor放在所有Interceptor的最面。

有了上述的Token Interceptor,可以防止客戶重複送出,大大地降低了伺服器的負荷。但是對使用者來說,可能會很不友善,一不小心點選了送出按鈕,進入到了invalid.token頁面,就再也回不去了,上述的操作就再也看不見了。

等待畫面

我們能不能提供一個等待畫面呢?

每次我們送出之後畫面立即遷移到一個類似狀态條的畫面,這個畫面不斷的向伺服器請求,以确定Action是否執行完畢,一旦Action執行完畢,立即定位到正确的畫面。這樣不是更好。

Struts2也提供了對等待畫面的支援。

首先當我們送出完成的時候,畫面會前一到一個叫做等待畫面的頁面。等待畫面定時的向伺服器送出請求,以确定伺服器操作是否完成。另外正在執行的Action需要有一個攔截對象,攔截等待頁面的每一次請求,告訴等待頁面是否處理完成。

首先我們需要定義,等待頁面和Action是否完成的攔截對象

<action name="longLivedAction" class="com.jpleasure.LongLivedAction">

<interceptor-ref name="completeStack"/>

<interceptor-ref name="execAndWait"/>

<result name="wait">/jsp/wait.jsp</result>

<result name="success">/jsp/after.jsp</result>

</action>

<result name="wait">/jsp/wait.jsp</result>定義了等待畫面是/jsp/wait.jsp,當我們向伺服器送出請求之後,畫面會遷移到這個畫面。

<interceptor-ref name="execAndWait"/>定義了一個攔截對象,告訴等待畫面是否完成了長時間操作。

那麼等待畫面如何定期的查詢呢?

之需要一個指向該Action調用的一個不斷地重新整理即可。代碼如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%@taglib prefix="s" uri="/struts-tags" %>

<html>

<head>

<title>Please wait</title>

<meta http-equiv="refresh" content="5;url=<s:url includeParams='all'/> "/>

</head>

<body>

</body>

</html>

Please wait while we process your request.

<a href="<s:url includeParams=" target="_blank" rel="external nofollow" all" />"> Click Here</a> if this page does not reload automatically.

上述黑體部分表示,一旦畫面建立,就建立了一個重新整理,每5秒鐘重新整理一次,重新整理的連結為

url=<s:url includeParams=”all”/>,meta是标準浏覽器支援的内容之一。

在運作的時候可以看到生成需下的内容:

<meta http-equiv="refresh"

content="5;url=/wait/jsp/longLivedAction.action?name=zhangsf&value=119 "/>

如果浏覽器不支援自動重新整理也不要緊,可以讓客戶自己點選Click Here來确定操作是否完成。

其實這裡最重要的是execAndWait Interceptor,每次向它攔截的Action發送請求的時候,execAndWait會确定Action操作是否完成,如果完成,将畫面定位到對應的頁面(通常是SUCCESS指向的頁面),否則指向wait指向的頁面。

繼續閱讀