ASP.NET 如何避免頁面重新整理時重複送出
来源:互联网 发布:艾瑞数据应用商店排名 编辑:程序博客网 时间:2024/06/06 19:57
原帖地址:http://www.dotblogs.com.tw/hunterpo/archive/2009/11/06/11453.aspx
有些使用者的行為真是令人猜不透…,開網頁有事沒事就來給你 Refresh 一下,這個動作看似無害,但是在剛執行過 Submit 的情況下,Refresh 網頁會造成重複執行,這也是為什麼在各大購物網站的交易付款動作,都會提示「不要關閉網頁或重新整理避免造成交易失敗或重複交易」這一類的訊息,但根據經驗,就算在網頁上提出警告了,仍有為數不少的使用者依然會 Refresh 網頁。
注意,別以為只有 ASP.NET 才有這種問題,這問題普遍存在於網頁程式,不管你用何種平台、語言開發,這肇因於瀏覽器會自行 Cache 使用者的瀏覽行為 (包含資料),測試過 IE、FireFox、Chrome 都一樣,猜想是因為這樣才能有上一頁、下一頁的歷程紀錄,至於更進一步的探討,小的力有未逮就不再深究 (歡迎瞭解的前輩高人指點一下迷津,說說緣由)…既然瀏覽器原始設計如此,而我們可能永遠都猜不透使用者愛怎麼操作,那就針對 ASP.NET 的開發來看看有甚麼方式可以解決這樣的問題。
不知道有沒有人跟我一樣,馬上想到的是:重新導向,也就是在執行某一工作成功之後,執行 Response.Redirect 方法重導到結果頁面,這是最典型的作法,不過這比較適用在動作連貫的多重網頁表單,例如:購物車,在完成結帳後就可以導到訂購成功的訊息頁面,反正前面也從第一步、第二步…到結帳畫面了,再多導一次已經差別不大。可惜多重網頁表單畢竟是少數,大部分網頁程式現在幾乎都要求非同步更新 (AJAX),最好在同一畫面完成所有動作,即便今天不要求非同步更新,每個作業完成之後都導到另一個網頁,也不甚理想,所以這種作法並不完美,除了多維護一個網頁的麻煩不說,事實上使用者若先回到上一頁再重新整理,一樣可能會造成重複執行…。
那在任何異動前,先檢查是否有相同資料存在呢?換句話說是在資料庫端檢查,應該可行,不過…過程似乎稍嫌繁瑣,要針對每一個作業內容個別去撰寫比對是否有相同資料的邏輯,光想就覺得累了…,況且有時候確實是可以允許相同資料存在,比如說線上客服的留言版,使用者不耐久候時,會再留言一次,內容可能跟前次一模一樣,這跟重刷頁面造成的資料重複是不可相提並論的,這樣看來在資料庫端排除相同資料也不是很好的作法…
關鍵點在於怎麼分辨出使用者正在重刷頁面,進一步地,有沒有一勞永逸的做法,讓我們可以檢查某一屬性就能判斷是不是重刷頁面所回傳,來避免重複送出動作?轉念一想:太陽底下無新事,上網搜尋了一下,國外有幾篇文章、討論串針對這問題提出了幾個解法 (事實證明前述兩種作法也是有人建議),其中我覺得最值得一看的是底下兩篇:
- Build Your ASP.NET Pages on a Richer Bedrock
- Preventing Duplicate Record Insertion on Page Refresh
- 繼承 System.Web.UI.Page,自訂一個 BasePage 類別。 1using System;23/// <summary>4/// BasePage 的摘要描述5/// </summary>6public class BasePage : System.Web.UI.Page7{8 public BasePage() { }9}
- 在 BasePage 類別底下撰寫 SetActionStamp 方法,目的是在 Session 存放一個系統時間戳記。 1/// <summary>2/// 設置戳記3/// </summary>4private void SetActionStamp()5{6 Session["actionStamp"] = Server.UrlEncode(DateTime.Now.ToString());7}
- 處裡 PreRender 事件,在網頁初次載入時設置戳記,且每次載入執行時會把該戳記存放到 HiddenField 裡。 01public BasePage() { this.PreRender += new EventHandler(Page_PreRender); }0203void Page_PreRender(object sender, EventArgs e)04{05 if (!IsPostBack)06 {07 SetActionStamp();08 }0910 ClientScript.RegisterHiddenField("actionStamp", Session["actionStamp"].ToString());11}
- 自訂 IsRefresh 屬性,藉由判斷 HiddenField 存放的戳記是否等於 Session 裡存放的值,就可以得知網頁是否經由重新整理動作回傳。01/// <summary>02/// 取得值,指出網頁是否經由重新整理動作回傳 (PostBack)03/// </summary>04protected bool IsRefresh05{06 get07 {08 if HttpContext.Current.Request["actionStamp"] as string == Session["actionStamp"] as string)09 {10 SetActionStamp();11 return false;12 }1314 return true;15 }16}
之後撰寫網頁程式時,只要衍生自 BasePage 就可以取得 IsRefresh 屬性值,可以用來判斷網頁是否被重新整理,避免重複執行之前的動作:
至於概念是這樣:網頁初次載入時我們在 Session 記錄時間戳記,Copy 一份到 HiddenField 存放起來,在頁面反覆執行時該戳記始終是初次設置的值,直到某一項動作我們希望可以辨別是否經由重新整理所送出,所以對 IsRefresh 屬性加以判斷 ,初次送出時當然會回傳 false,作業可以順利執行,連帶只更新 Session 裡的時間戳記,這時已不同 HiddenField 裡的值。有趣的事情來了,Refresh 時把前次動作再送出一次,但因為瀏覽器會 Cache 狀態,這時 HiddenField 裡的時間戳記依然是較舊的值,不同於 Session 所持有的,檢查 IsRefresh 屬性值是 true,為了避免重複執行就可以把動作擋下來。
目前為止,本文就標題所提的問題提出解法並簡單說明了概念,但前面所提供的那兩篇參考文章其實有詳細的闡述,想了解的人建議一定要去看看,特別是第一篇由大師 Dino Esposito(介紹、個人部落格) 所寫的文章。
最後說一個 Dino 大師的文章有提到的小技巧,既然我們擴充了一個有偵測頁面重新整理功能的 BasePage 類別,那要怎麼讓之後新加入的 Web Form 預設都是由 BasePage 衍生而來?可以打開 web.config 檔裡 <system.web> 底下的 <pages> 設定 pageBaseType 屬性,例如:
設定完成之後,往後新加入的 Web Form 就會改繼承自 BasePage。
- ASP.NET 如何避免頁面重新整理時重複送出
- ASP.NET避免页面重新整理时重复发送...
- 如何避免ASP.NET缓存占用资源?
- 如何重新注册asp.net 2.0
- 如何重新注册asp.net 2.0
- ASP.NET面试题 最新整理
- ASP.NET经典面试题 整理之一
- 如何避免asp.net中的页面闪动问题!
- asp.net如何避免页面html代码转义
- .net 面试题整理
- .net面试题整理
- 重新注册一下ASP.NET
- asp.net URL重新实例
- 整理: ASP.NET防盗链IHttpHandler源码如何做的?
- ASP.NET 2.0 中的主版頁面
- [asp.net]頁面列印9/20
- ASP.NET頁面事件執行順序
- 我的asp重新整理 - by yzty
- 数据结构——算法之(037)(数值的整数次方)
- 刀净骋倏恼灯幻季灰哨杏跋聊秦裁
- MODULE_DEVICE_TABLE的理解
- 学生有逃课的自由 (2008-01-13 21:29:48)
- 开发过程中的常用Eclipse插件
- ASP.NET 如何避免頁面重新整理時重複送出
- Android Json数据解析
- unity3d 英保通公开课小结 1-38讲
- RobotiumRecord的使用
- Visual Studio Autos 自动 窗口
- 算法基础和复杂度
- 工作日志2014-06-30
- interface与abstract class区别
- Android的布局