asp.net 檢測頁面是否重新整理

來源:互聯網
上載者:User

標籤:.net   asp.net   重新整理檢測   hashtable   



來分析這樣一種實際情況,即,在HTTP處理常式處理請求之前對請求進行篩選,這有助於實現一個原本不可能的特徵。回傳機制有一個嚴重的缺陷——如果使用者重新整理當前顯示頁面,則伺服器上所採取的最後一個動作將盲目地重複。例如,如果作為前一次發送的結果添加了一個新記錄,則應用程式會在另一次回傳時試圖插入一個完全相同的記錄。當然,這會導致插入完全相同的記錄,因而應當產生一個異常。這一缺陷自Web編程最先出現時就已經存在了,ASP.NET無疑不會引入它。要實現非重複的動作,必須採取一些對策,本質上將任何關鍵的伺服器端操作轉換為一個等冪性。在代數中,如果一個操作不管對它執行多少次結果都不變,我們就說該操作是等冪的。例如,看一看如下SQL命令:

DELETE FROM employees WHERE employeeid=9

我們可以對該命令連續執行1000次,但是最多隻會刪掉1個記錄,即滿足WHERE子句中設定的標準的記錄。另請考慮如下命令:

INSERT INTO employees VALUES (...)

每次執行該命令,都有可能把一個新記錄添加到employees表中。如果存在自動編碼的鍵列或者非惟一的列,尤其會出現這種情況。如果表設計要求鍵是惟一的並且明確加以規定,則第2次運行該命令時會拋出一個SQL異常。

雖然剛才考慮的特殊情況通常在資料訪問層(data access layer,簡稱DAL)解決,但是它的基本模式代表了大多數Web應用程式的一個常見方案。因此,待研究的問題是:怎樣查明頁面是因為一個顯式的使用者操作被回傳,還是因為使用者按下了F5鍵或頁面重新整理工具列按鈕呢?

1. 頁面重新整理操作的基本原理

頁面重新整理操作是一種內部瀏覽器操作,對此瀏覽器不會根據事件或回調提供任何外部通知。從技術上講,頁面重新整理是由最新請求的“簡單的”重複的群組成的。瀏覽器緩衝它所服務的最新請求,並在使用者按下頁面重新整理鍵或按鈕時重新顯示。我所知道的瀏覽器不會為頁面重新整理事件提供任何類型通知——即使有,無疑也不是一種公認標準。

據此可知,伺服器端代碼(例如,ASP.NET、經典ASP或ISAPI DLL)無法將重新整理請求與一般的提交或回傳請求相區分。為了協助ASP.NET檢測和處理頁面重新整理,我們需要建立外圍機制,使兩個在其他方面相同的請求看起來不同。所有已知的瀏覽器都是通過重新發送最後發送的HTTP請求來實現重新整理;為了使該副本不同於原始請求,一個額外的服務必須添加其他參數,而 ASP.NET頁面必須能夠捕獲它們。

我考慮了一些附加需求。解決方案不應依賴工作階段狀態,而且不應使伺服器記憶體負荷太重。它應該是相對容易部署的,而且應盡量不引人注目。

2. 解決方案的概要描述

本解決方案基於如下思想:每個請求被分配一個標籤號,而HTTP模組將跟蹤它處理的每個不同頁面裡最後服務的標籤。如果該頁面持有的標籤號小於該頁面的最後服務的標籤,則只能表明服務了相同的請求——即,頁面重新整理。該解決方案由兩個構造塊組成:一個HTTP模組和一個自訂的頁面類,前者對標籤號作初步檢查,後者自動地將一個漸進的標籤號碼添加到每個服務過的頁面。使該特徵起作用涉及兩個步驟:首先,註冊該HTTP模組;其次,在相關的應用程式中改變每個頁面的基本的程式碼後置類別以檢測瀏覽器重新整理。

HTTP模組位於HTTP運行庫環境的中間,登記應用程式中的一個資源的每個請求。頁面第一次被請求時(不是回傳時),不分配任何標籤。HTTP模組將產生一個新的標籤號,並把它儲存在HttpContext對象的Items集合中。此外,該模組將最後服務的標籤的內部計數器初始化為0。隨後該頁面每次被請求時,該模組都將最後服務的標籤與頁面標籤進行比較。如果頁面標籤更新一些,則該請求被認為是一次普通的回傳;否則,它將被標記為一次頁面重新整理。表2.6總結了這兩種情境及其相關的操作。




為了確保每個請求(除了第一次以外)都有一個合適的標籤號,需要得到頁面類的一些協助。這就是為什麼需要將每個打算支援該特徵的頁面的程式碼後置類別設定為一個特定類——這是我們稍候將討論的一個過程。該頁面類將從HTTP模組接收兩種不同的資訊:要儲存在隨頁面一起傳送的一個隱藏欄位中的下一個標籤,以及該請求是否為頁面重新整理的資訊。作為對開發人員的一項增值服務,程式碼後置類別將提供一個額外的布爾屬性:IsRefreshed,以允許開發人員瞭解請求是頁面重新整理還是常規回傳。

*重要提示    HttpContext類上的Items集合是一個載體集合,是為了讓HTTP模組將資訊向下傳遞給實際負責服務要求的頁面和HTTP處理常式而特意建立的。我們這裡採用的HTTP模組在Items集合中設定兩個資料項目。一個資料項目讓頁面知道請求是否為頁面重新整理;另一個資料項目讓頁面知道下一個標籤號是什麼。讓HTTP模組將下一個標籤號傳遞給頁面,滿足使頁面類的行為儘可能地簡單和線性目的,從而將大部分實現和執行負擔轉移給HTTP模組。

3. 解決方案的實現

我剛剛概述的解決方案有幾個問題有待研究。首先,狀態是必需的,我們把它儲存在哪裡?其次,對每個輸入請求都將調用一個HTTP模組。如何區分對相同頁面的請求呢?如何把資訊傳遞給頁面呢?你希望頁面有多大的智能呢?

顯然,這裡所列的每個問題,都可以用不同於此處所介紹的方法進行設計和實現。為了得到一個可行的解決方案,這裡作出的所有設計選擇應當被認為是任意的,如果需要對該代碼進行重新加工以更好地滿足自己的目的,可以用等效的策略替換它。下一個執行個體中給出的代碼版本,融入了我一直以來所收集的最寶貴的建議。這些建議之一如前一個重要提示所述,盡量將代碼移到HTTP模組中。

public class RefreshModule : IHttpModule{public void Init(HttpApplication app) {app.BeginRequest += new EventHandler(OnAcquireRequestState);}public void Dispose() {}void OnAcquireRequestState(object sender, EventArgs e) {HttpApplication app = (HttpApplication) sender;HttpContext ctx = app.Context;RefreshAction.Check(ctx);return;}
}

該模組監聽BeginRequest事件,結束調用RefreshAction輔助類上的Check方法。 

public class RefreshAction{static Hashtable requestHistory = null;// Other string constants defined herepublic static void Check(HttpContext ctx) {// Initialize the ticket slotEnsureRefreshTicket(ctx);// Read the last ticket served in the session (from Session)int lastTicket = GetLastRefreshTicket(ctx);// Read the ticket of the current request (from a hidden field)int thisTicket = GetCurrentRefreshTicket(ctx, lastTicket);// Compare ticketsif (thisTicket > lastTicket ||(thisTicket==lastTicket && thisTicket==0)) {UpdateLastRefreshTicket(ctx, thisTicket);ctx.Items[PageRefreshEntry] = false;}elsectx.Items[PageRefreshEntry] = true;}// Initialize the internal data storestatic void EnsureRefreshTicket(HttpContext ctx){if (requestHistory == null)requestHistory = new Hashtable();}// Return the last-served ticket for the URLstatic int GetLastRefreshTicket(HttpContext ctx){// Extract and return the last ticketif (!requestHistory.ContainsKey(ctx.Request.Path))return 0;elsereturn (int) requestHistory[ctx.Request.Path];}// Return the ticket associated with the pagestatic int GetCurrentRefreshTicket(HttpContext ctx, int lastTicket){int ticket;object o = ctx.Request[CurrentRefreshTicketEntry];if (o == null)ticket = lastTicket;elseticket = Convert.ToInt32(o);ctx.Items[RefreshAction.NextPageTicketEntry] = ticket + 1;return ticket;}// Store the last-served ticket for the URLstatic void UpdateLastRefreshTicket(HttpContext ctx, int ticket){requestHistory[ctx.Request.Path] = ticket;}}
Check方法操作如下:它將最後服務的標籤(如果有)與頁面提供的標籤進行比較。該頁面將標籤號儲存在一個通過Request對象介面讀入的隱藏欄位中。HTTP模組維護一個散列表,服務的每個不同的URL都有一個表項。該散列表中的值儲存該URL的最後服務的標籤。

注意    Item索引器屬性,來設定最後服務的標籤,因為Item重寫已有的項。如果資料項目已經存在,則Add方法只是返回。

除了建立HTTP模組,我們還需要安排一個頁面類,以用作需要檢測瀏覽器重新整理的頁面的基類。下面給出了這個頁面類的代碼:
// Assume to be in a custom namespacepublic class Page : System.Web.UI.Page{public bool IsRefreshed {get {HttpContext ctx = HttpContext.Current;object o = ctx.Items[RefreshAction.PageRefreshEntry];if (o == null)return false;return (bool) o;}}// Handle the PreRenderComplete eventprotected override void OnPreRenderComplete(EventArgs e) {base.OnPreRenderComplete(e);SaveRefreshState();}// Create the hidden field to store the current request ticketprivate void SaveRefreshState() {HttpContext ctx = HttpContext.Current;int ticket = (int) ctx.Items[RefreshAction.NextPageTicketEntry];ClientScript.RegisterHiddenField(RefreshAction.CurrentRefreshTicketEntry,ticket.ToString());}}

該樣本頁面定義了一個新的公用布爾屬性IsRefreshed。我們可以在代碼中以使用IsPostBack或IsCallback那樣的方法使用該屬性。該執行個體頁面重寫了OnPreRenderComplete方法,用頁面標籤添加隱藏欄位。如前所述,該頁面標籤是通過Items集合中的一個特別的(並且是任意命名的)項從HTTP模組中得到的。

 
public partial class TestRefresh : ProAspNet20.CS.Components.Page{protected void AddContactButton_Click(object sender, EventArgs e){Msg.InnerText = "Added";if (!this.IsRefreshed)AddRecord(FName.Text, LName.Text);elseMsg.InnerText = "Page refreshed";BindData();}}

IsRefreshed屬性允許我們決定在一個回傳動作被請求時要做什麼。在上述代碼中,如果頁面正在重新整理,則不調用AddRecord方法。不用說,IsRefreshed僅適用於這裡介紹的自訂頁面類。自訂頁面類並非只是添加該屬性,它還要添加隱藏欄位,這是該機制起作用所必不可少的。



asp.net 檢測頁面是否重新整理

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.