在 ASP.NET 中實現工作階段狀態的基礎

來源:互聯網
上載者:User
在 ASP.NET 中實現工作階段狀態的基礎

Dino Esposito
Wintellect

2003 年 9 月

適用於:
Microsoft ASP.NET

摘要:討論如何在 ASP.NET 1.1 中實現工作階段狀態功能,以及如何在被管理的 Web 應用程式中最佳化工作階段狀態管理。(本文包含一些指向英文網站的連結。)

目錄

簡介
ASP.NET 工作階段狀態概述
同步訪問工作階段狀態
比較狀態供應器
狀態序列化和還原序列化
會話的生命週期
Cookieless 會話
小結

簡介

在 Web 應用程式這樣的無狀態環境中,瞭解工作階段狀態的概念並沒有實際的意義。儘管如此,有效狀態管理對於大多數 Web 應用程式來說都是一個必備的功能。Microsoft ASP.NET 以及許多其他伺服器端編程環境都提供了一個抽象層,允許應用程式基底於每個使用者和每個應用程式儲存持久性資料。

需要特別注意的是,Web 應用程式的工作階段狀態是應用程式在不同的請求中緩衝和檢索的資料。會話表示使用者在與該網站串連期間發送的所有請求,工作階段狀態是使用者在會話期間產生和使用的持久性資料的集合。每個會話的狀態都彼此獨立,而且在使用者會話結束時就不複存在了。

工作階段狀態與構成 HTTP 協議和規範的任何邏輯實體都沒有對應關係。會話是由伺服器端開發環境(例如傳統的 ASP 和 ASP.NET)構建的抽象層。ASP.NET 展示工作階段狀態的方式以及工作階段狀態的內部實現方式都取決於平台的基礎結構。因此,傳統的 ASP 和 ASP.NET 以完全不同的方式來實現工作階段狀態,預計在下一版的 ASP.NET 中會有進一步的改進和增強。

本文討論如何在 ASP.NET 1.1 中實現工作階段狀態,以及如何在被管理的 Web 應用程式中最佳化工作階段狀態管理。

ASP.NET 工作階段狀態概述

工作階段狀態並不是 HTTP 基礎結構的一部分。也就是說,應該有一個結構組件將工作階段狀態與每個傳入請求綁定在一起。運行時環境(傳統的 ASP 或 ASP.NET)能夠接受 Session 之類的關鍵字,並使用它指示伺服器上儲存的資料區塊。要成功解析 Session 對象的調用,運行時環境必須將工作階段狀態添加到正在處理的請求的調用上下文中。完成此操作的方式因平台而異,但它是有狀態 Web 應用程式的基礎操作。

在傳統的 ASP 中,工作階段狀態是作為 asp.dll 庫中包含自由線程 COM 物件來實現的。(您對此很好奇嗎?其實該對象的 CLSID 是 D97A6DA0-A865-11cf-83AF-00A0C90C2BD8。)此Object Storage Service以成對的名稱和數值集合的方式組織的資料。“名稱”預留位置表示用來檢索資訊的關鍵字,而“值”預留位置表示工作階段狀態中儲存的內容。成對的名稱和數值按照會話 ID 進行分組,這樣,每個使用者看到的只是他/她自己建立的成對的名稱和數值。

在 ASP.NET 中,工作階段狀態的編程介面與傳統的 ASP 幾乎是相同的。但它們的基礎實現是完全不同的,前者比後者更具有靈活性、可擴充性和更強的編程功能。深入研究 ASP.NET 工作階段狀態之前,讓我們簡單回顧一下 ASP.NET 會話基礎結構的某些結構功能。

在 ASP.NET 中,任何傳入 HTTP 要求都要通過 HTTP 模組管道進行傳輸。每個模組都可以篩選並修改請求所攜帶的大量資訊。與每個請求關聯的資訊叫做“調用上下文”,編程中用 HttpContext 對象來表示。我們不應將請求的上下文視為狀態資訊的另一個容器,雖然它提供的 Items 集合只是一個資料容器。HttpContext 對象不同於所有其他狀態物件(例如,SessionApplicationCache),因為它的有限生命週期超出了處理請求所需的時間。當請求通過一系列註冊的 HTTP 模組後,其 HttpContext 對象將包含狀態物件的引用。當最終可以處理請求時,關聯的調用上下文將綁定到特定會話 (Session) 和全域狀態物件(ApplicationCache)。

負責設定每個使用者的工作階段狀態的 HTTP 模組為 SessionStateModule。該模組的結構是根據 IHttpModule 介面設計的,它為 ASP.NET 應用程式提供大量與工作階段狀態有關的服務。包括產生會話 ID、Cookieless 會話管理、從外部狀態供應器中檢索會話資料以及將資料繫結到請求的調用上下文。

HTTP 模組並不在內部儲存會話資料。工作階段狀態始終儲存在名為“狀態供應器”的外部組件中。狀態供應器完全封裝工作階段狀態資料,並通過 IStateClientManager 介面的方法與其他部分進行通訊。工作階段狀態 HTTP 模組調用該介面上的方法來讀取並儲存工作階段狀態。ASP.NET 1.1 支援三種不同的狀態供應器,如表 1 所示。

表 1:狀態用戶端提供者

提供者 說明
InProc 會話值在 ASP.NET 輔助進程(Microsoft Windows Server 2003 中的 aspnet_wp.exew3wp.exe)的記憶體中保持為使用中的物件。這是預設選項。
StateServer 會話值被序列化並儲存在單獨進程 (aspnet_state.exe) 的記憶體中。該進程還可以在其他電腦上運行。
SQLServer 會話值被序列化並儲存在 Microsoft SQL Server 表中。SQL Server 的執行個體可以在本地運行,也可以遠程運行。

工作階段狀態 HTTP 模組將從 web.config 檔案的 <sessionState> 部分讀取當前選定的狀態供應器。

<sessionState mode="InProc | StateServer | SQLServer />

根據 mode 特性的值,將通過不同的步驟從不同的進程中檢索工作階段狀態並將其儲存到不同的進程中。預設情況下,工作階段狀態儲存在本地的 ASP.NET 輔助進程中。特殊情況下,會將其儲存在 ASP.NET Cache 對象的專用槽中(不能通過編程方式訪問)。也可以將工作階段狀態儲存在外部,甚至是遠程進程中(例如,名為 aspnet_state.exe 的 Windows NT 服務中)。第三個選項是將工作階段狀態儲存到由 SQL Server 2000 管理的專用資料庫表中。

HTTP 模組會在請求的一開始對會話值進行還原序列化,使它們成為詞典對象。然後,將採用編程方式通過類(例如,HttpContextPage)顯示的屬性 Session 來訪問詞典(實際上是 HttpSessionState 類型的對象)。工作階段狀態值與開發人員可見的會話對象之間的綁定將持續到請求結束。如果請求成功完成,所有狀態值將被序列化回狀態供應器,並可用於其他請求。

圖 1 說明了請求的 ASP.NET 頁面與會話值之間的通訊。每個頁面所使用的代碼都與 page 類上的 Session 屬性有聯絡。其編程方式與傳統的 ASP 幾乎相同。

圖 1:ASP.NET 1.1 中的工作階段狀態體繫結構

在完成請求所需的時間內,工作階段狀態的物理值處於鎖定狀態。該鎖定由 HTTP 模組在內部管理並用於同步對工作階段狀態的訪問。

工作階段狀態模組執行個體化應用程式的狀態供應器,並使用從 web.config 檔案中讀取的資訊對其進行初始化。接下來,每個提供者將繼續自己的初始化操作。提供者的類型不同,其初始化操作會大不相同。例如,SQL Server 狀態管理器將開啟與給定資料庫的串連,而進程外管理器將檢查指定的 TCP 通訊埠。另一方面,InProc 狀態管理器將儲存對回呼函數的引用。從緩衝中刪除元素時將執行此操作,並用於觸發應用程式的 Session_OnEnd 事件。

同步訪問工作階段狀態

當 Web 頁對 Session 屬性進行非常簡單且直觀的調用時,究竟會出現什麼情況呢?許多操作都是在後台進行的,如下面的繁瑣代碼所示:

int siteCount = Convert.ToInt32(Session["Counter"]);

上述代碼實際上訪問的是 HTTP 模組建立的會話值在本地記憶體中的副本,從特定狀態供應器(參見圖 1)中讀取資料。如果其他頁面也試圖同步訪問該工作階段狀態,又會如何呢?這種情況下,當前的請求可能會停止處理不一致的資料或過時的資料。為了避免這種情況,工作階段狀態模組將實現一個讀取器/寫入器鎖定機制,並對狀態值的訪問進行排隊。對工作階段狀態具有寫入許可權的頁面將保留該會話的寫入器鎖定,直到請求終止。

通過將 @Page 指令的 EnableSessionState 屬性設定為 true,頁面可以請求工作階段狀態的寫入許可權。(這是預設設定)。但是,頁面還可以擁有工作階段狀態的唯讀許可權,例如,當 EnableSessionState 屬性被設定為 ReadOnly 時。在這種情況下,模組將保留該會話的讀取器鎖定,直到該頁面的請求結束。結果將發生並發讀取。

如果頁面請求設定一個讀取器鎖定,同一會話中同時處理的其他請求將無法更新工作階段狀態,但是至少可以進行讀取。也就是說,如果當前正在處理會話的唯讀請求,那麼等候的唯讀請求要比需要完整存取的請求具有更高的優先權。如果頁面請求為工作階段狀態設定一個寫入器鎖定,那麼所有其他頁面都將被阻止,無論它們是否要讀取或寫入內容。例如,如果同時有兩個架構試圖在 Session 中寫入內容,一個架構必須等到另一個架構完成後才能寫入。

比較狀態供應器

預設情況下,ASP.NET 應用程式將工作階段狀態儲存在輔助進程的記憶體中,特別是 Cache 對象的專用槽中。選中 InProc 模式時,工作階段狀態將儲存在 Cache 對象內的槽中。此槽被標記為專用槽,無法通過編程方式進行訪問。換句話說,如果枚舉 ASP.NET 資料緩衝中的所有項目,將不會返回類似於給定工作階段狀態的任何對象。Cache 對象提供兩類槽:專用槽和公用槽。編程人員可以添加和處理公用槽,但專用槽只能由系統(特別是 system.web 組件中定義的類)專用。

每個活動會話的狀態都佔用緩衝中的一個專用槽。槽的名稱根據會話 ID 進行命名,其值是名為 SessionStateItem 的內部未聲明類的一個執行個體。InProc 狀態供應器擷取會話 ID 並在緩衝中檢索對應的元素。然後將 SessionStateItem 對象的內容輸入 HttpSessionState 詞典對象,並由應用程式通過 Session 屬性進行訪問。請注意,ASP.NET 1.0 中存在一個錯誤,使 Cache 對象的專用槽可以通過編程方式進行枚舉。如果您在 ASP.NET 1.0 下運行以下代碼,則能夠枚舉與每個當前活動工作階段狀態中包含的對象對應的項目。

foreach(DictionaryEntry elem in Cache){   Response.Write(elem.Key + ": " + elem.Value.ToString());}

此錯誤已經在 ASP.NET 1.1 中得到解決,當您枚舉緩衝的內容時,將不再列出任何系統槽。

到目前為止,InProc 可能是最快的訪問選項。但請記住,會話中儲存的資料越多,Web 服務器所消耗的記憶體就越多,這樣會潛在地增加效能降低的風險。如果您計劃使用任何進程外解決方案,應該認真考慮一下序列化和還原序列化可能帶來的影響。進程外解決方案使用 Windows NT 服務 (aspnet_state.exe) 或 SQL Server 表來儲存會話值。因此,工作階段狀態保留在 ASP.NET 輔助進程之外,並且需要使用額外的代碼層,在工作階段狀態和實際的儲存介質之間進行序列化和還原序列化操作。只要處理請求就會發生此操作,而且隨後必須對其進行最高程度的最佳化。

因為需要將會話資料從外部儲備庫複製到本地會話詞典中,所以請求導致效能下降了 15%(進程外)到 25% (SQL Server)。請注意,雖然這隻是一種粗略的估計,但它應該接近於最低程度的影響,最高程度的影響將遠高於此。實際上,這種估計並沒有完全考慮到工作階段狀態中實際儲存的類型的複雜程度。

在進程外儲存方案中,工作階段狀態存活的時間較長,使應用程式的功能更強大,因為它可以防止 Microsoft Internet 資訊服務 (IIS) 和 ASP.NET 失敗。通過將工作階段狀態與應用程式相分離,您還可以更容易地將現有應用程式擴充到 Web Farm 和 Web Garden 體繫結構中。另外,工作階段狀態儲存在外部進程中,從根本上消除了由於進程迴圈而導致的周期性資料丟失的風險。

下面介紹如何使用 Windows NT 服務。正如上文所述,NT 服務是一個名為 aspnet_state.exe 的進程,通常位於 C:\WINNT\Microsoft.NET\Framework\v1.1.4322 檔案夾中。

實際目錄取決於您實際啟動並執行 Microsoft .NET Framework 版本。使用狀態伺服器之前,應確保該服務就緒並正運行在用作會話存放裝置的本地或遠端電腦上。狀態服務是 ASP.NET 的組成部分並與之一起安裝,因此您無需運行其他安裝程式。預設情況下,狀態服務並沒有運行,需要手動啟動。ASP.NET 應用程式將在載入狀態伺服器之後立即嘗試與之建立串連。因此,該服務必須準備就緒且正在運行,否則將引發 HTTP 異常。顯示了該服務的屬性對話方塊。

圖 2:ASP.NET 狀態伺服器的屬性對話方塊

ASP.NET 應用程式需要指定工作階段狀態服務所在的電腦的 TCP/IP 位址。必須將以下設定輸入該應用程式的 web.config 檔案中。

<configuration>    <system.web>        <sessionState             mode="StateServer"             stateConnectionString="tcpip=expoware:42424" />    </system.web></configuration>

stateConnectionString 特性包含電腦的 IP 位址以及用來進行資料交換的連接埠。預設的電腦地址為 127.0.0.1(本地主機),預設連接埠為 42424。您也可以按名稱指示電腦。對於代碼來說,使用本地或遠端電腦是完全透明的。請注意,不能在該名稱中使用非 ASCII 字元,並且連接埠號碼是強制的。

如果您使用進程外會話儲存,工作階段狀態將仍然存在並且可供將來使用,無論 ASP.NET 輔助進程出現何種情況。如果該服務被中斷,資料將被保留下來,並且在該服務恢複時自動進行檢索。但是,如果狀態供應器服務停止或失敗,資料將丟失。如果您希望應用程式具有強大的功能,請使用 SQLServer 模式,而不要使用 StateServer 模式。

<configuration>    <system.web>        <sessionState             mode="SQLServer"             sqlConnectionString="server=127.0.0.1;uid=<user id>;pwd=<password>;" />    </system.web></configuration>

您可以通過 sqlConnectionString 特性指定連接字串。請注意,特性字串必須包含使用者識別碼、密碼和伺服器名稱。它不能包含 Database 和 Initial Catalog 之類的標記,因為此資訊預設為固定名稱。使用者識別碼 和密碼可以替換為整合的安全設定。

如何建立資料庫?ASP.NET 提供兩對指令碼來設定資料庫環境。第一對指令碼名為 InstallSqlState.sqlUninstallSqlState.sql,與工作階段狀態 NT 服務位於同一個檔案夾中。它們建立名為 ASPState 的資料庫和幾個儲存的過程。但是,資料存放區在 SQL Server 臨時儲存地區 TempDB 資料庫中。這意味著,如果重新啟動 SQL Server 電腦,會話資料將丟失。

要解決這一局限性,請使用第二對指令碼。第二對指令碼名為 InstallPersistSqlState.sqlUninstallPersistSqlState.sql。在這種情況下,將建立 ASPState 資料庫,但是會在同一個資料庫中建立資料表,而且這些資料表同樣是持久的。為會話安裝 SQL Server 支援時,還將建立一個作業,以刪除工作階段狀態資料庫中到期的會話。該作業名為 ASPState_Job_DeleteExpiredSessions 並且一直運行。請注意,要使該作業正常進行,需要運行 SQLServerAgent 服務。

無論您選擇哪種模式,為工作階段狀態操作進行編碼的方式都不會改變。您可以始終針對 Session 屬性進行工作並像平常一樣讀取和寫入值。所有行為上的差異都是在較低的抽象層上處理的。狀態序列化或許是會話模式之間的最重要差異。

狀態序列化和還原序列化

使用進程內模式時,對象作為各自類的活動執行個體儲存在工作階段狀態中。如果未發生真正的序列化和還原序列化,則表示您實際上可以在 Session 中儲存您建立的任何對象(包括無法序列化的對象和 COM 物件),並且訪問它們的開銷也不會太高。如果您選擇進程外狀態供應器,又是另外一種情況。

在進程外體繫結構中,會話值將從本機存放區介質(外部 AppDomain 資料庫)複製到處理請求的 AppDomain 的記憶體中。需要使用序列化/還原序列化圖層完成該任務,並表示進程外狀態供應器的某項主要成本。這種情況對代碼產生的主要影響是只能在會話詞典中儲存可序列化的對象。

根據所涉及的資料類型,ASP.NET 使用兩種方法對資料進行序列化和還原序列化。對於基本類型,ASP.NET 使用經過最佳化的內部序列化程式;對於其他類型(包括對象和使用者定義的類),ASP.NET 使用 .NET 二進位格式化程式。基本類型包括字串日期時間布爾值位元組字元以及所有的數字類型。對於這些類型,使用量身製作的序列化程式要比使用預設的常用 .NET 二進位格式化程式更快。

經過最佳化的序列化程式沒有公開發布,也沒有以文檔形式提供。它僅僅是二進位讀取器/寫入器,並且使用簡單但有效儲存架構。該序列化程式使用 BinaryWriter 類寫入一個位元組表示類型,然後寫入一個位元組表示該類型對應的值。讀取序列化的位元組時,該類首先提取一個位元組,檢測要讀取的資料類型,然後對 BinaryReader 類調用特定類型的 ReadXxx 方法。

請注意,布爾值和數字類型的大小是眾所周知的,但對字串並非如此。在基礎資料流上,字串始終帶有一個固定長度的首碼(一次編寫 7 位整數代碼),讀取器根據這一事實來確定字串的正確大小。而日期值是通過唯寫入構成日期的標記總數來儲存的。因此,要對會話執行序列化操作,日期應為 Int64 類型。

只要將包含的類標記為可序列化的類,便可以使用 BinaryFormatter 類對更複雜的對象(以及自訂對象)執行序列化操作。所有非基本類型都採用相同的類型 ID 進行標識並與基本類型儲存在同一個資料流中。總之,序列化操作會導致效能下降 15% 至 25%。但請注意,這是基於假定使用基本類型所進行的粗數量級估計。使用的類型越複雜,開銷越大。

如果不大量使用基本類型,很難實現有效會話資料存放區。因此,至少在理論上,使用三個會話槽儲存對象的三個不同的字串屬性要比對整個對象進行序列化好。但是,如果要序列化的對象包含 100 個屬性,那該怎麼辦呢?是要使用 100 個槽,還是只使用一個槽?在許多情況下,更好的方法是將複雜的類型轉換為多個簡單的類型。這種方法基於類型轉換器。“類型轉換器”是一種輕便的序列化程式,它以字串集合的形式傳回型別的關鍵屬性。類型轉換器是使用特性與基類綁定在一起的外部類。由類型編寫者決定儲存哪些屬性以及如何儲存。類型轉換器對於 ViewState 儲存也有協助,它代表的是比二進位格式化程式更有效會話儲存方法。

會話的生命週期

關於 ASP.NET 會話管理,重要的一點是,僅當將第一個項目添加到記憶體詞典中時,工作階段狀態對象的生命週期才開始。僅在執行如下代碼片斷後,才可以認為 ASP.NET 會話開始。

Session["MySlot"] = "Some data";

Session 詞典通常包含 Object 類型,要向後讀取資料,需要將返回的值轉換為更具體的類型。

string data = (string) Session["MySlot"];

當頁面將資料儲存到 Session 中時,會將值載入到 HttpSessionState 類包含的特製的詞典類中。完成當前處理的請求時,會將詞典的內容載入到狀態供應器中。如果由於未通過編程方式將資料放入詞典而導致工作階段狀態為空白,則不會將資料序列化到儲存介質中,而且更重要的是,不會在 ASP.NET Cache、SQL Server 或 NT 狀態服務中建立槽來跟蹤當前會話。這是出於效能方面的原因,但會對處理會話 ID 的方式產生重要影響:將為每個請求產生一個新的會話 ID,直到將某些資料存放區到會話詞典中。

需要將工作階段狀態與正在處理的請求串連時,HTTP 模組會檢索會話 ID(如果它不是啟動請求),並在配置的狀態供應器中尋找它。如果沒有返回資料,HTTP 模組將為請求產生一個新的會話 ID。這可以很容易地通過以下頁面進行測試:

<%@ Page Language="C#" Trace="true" %></html><body><form runat="server"><asp:button runat="server" text="Click" /></form></body></html>

無論何時單擊該按鈕並返回頁面,都將產生新的會話 ID,同時記錄跟蹤資訊。

圖 3:在沒有將資料存放區到會話詞典中的應用程式中,為每個請求產生一個新的會話 ID。

Session_OnStart 事件的情況如何呢?也會為每個請求引發該事件嗎?如果應用程式定義 Session_OnStart 處理常式,則會始終儲存工作階段狀態,即使工作階段狀態為空白。因此,對於第一個請求之後的所有請求來說,會話 ID 始終為常量。僅在確實必要時,才使用 Session_OnStart 處理常式。

如果會話逾時或被放棄,下次訪問無狀態應用程式時,其會話 ID 不會發生改變。經過設計後,即使工作階段狀態到期,會話 ID 也能持續到瀏覽器會話結束。也就是說,只要瀏覽器執行個體相同,就始終使用同一個會話 ID 表示多個會話。

Session_OnEnd 事件標誌著會話的結束,並用於執行終止該會話所需的所有清除代碼。但請注意,只有 InProc 模式支援該事件,也就是說,只有將會話資料存放區在 ASP.NET 輔助進程中時才支援該事件。對於要引發的 Session_OnEnd 事件來說,必須首先存在工作階段狀態,這意味著必須在該工作階段狀態中儲存一些資料,並且必須至少完成一個請求。

在 InProc 模式下,作為項目添加到緩衝中的工作階段狀態被賦予一個可變到期時間策略。可變到期時間表示如果某個項目在一定時間內沒有使用,將被刪除。在此期間處理的任何請求的到期時間都將被重設。工作階段狀態項目的時間間隔被設定為會話逾時。用來重設工作階段狀態到期時間的技術非常簡單和直觀:會話 HTTP 模組唯讀取 ASP.NET Cache 中儲存的工作階段狀態項目。如果知道 ASP.NET Cache 對象的內部結構,該模組將進行計算以重新設定可變到期時間。因此,當快取項目到期時,會話已逾時。

到期的項目將自動從緩衝中刪除。狀態會話模組作為此項目的到期時間策略的一部分,也代表了一個刪除回呼函數。緩衝將自動調用刪除函數,刪除函數然後將引發 Session_OnEnd 事件。如果應用程式通過進程外組件來執行會話管理,則永遠不會引髮結束事件。

Cookieless 會話

每個活動 ASP.NET 會話都是使用僅由 URL 允許的字元組成的 120 位字串標識的。會話 ID 是使用隨機數產生器 (RNG) 加密提供者產生的。該服務提供者返回一個包含 15 個隨機產生數的序列(15 位元組 x 8 位 = 120 位)。隨機數數組然後被映射到有效 URL 字元並以字串形式返回。

會話 ID 字串被發送到瀏覽器,然後通過以下兩種方式之一返回伺服器應用程式:使用 Cookie(就像在傳統 ASP 中一樣)或經過修改的 URL。預設情況下,工作階段狀態模組將在用戶端建立 HTTP Cookie,但是可以使用嵌入會話 ID 字串的修改後的 URL(特別是對於不支援 Cookie 的瀏覽器)。採用哪種方法取決於應用程式的 web.config 檔案中所儲存的配置設定。要配置會話設定,可以使用 <sessionState> 區段和 Cookieless 特性。

<sessionState cookieless="true|false" />

預設情況下,Cookieless 特性為 false,表示使用了 Cookie。實際上,Cookie 只是 Web 頁放在用戶端硬碟上的一個文字檔。在 ASP.NET 中,Cookie 由 HttpCookie 類的一個執行個體來表示。通常,Cookie 包含名稱、值集合和到期時間。Cookieless 特性被設定為 false 時,工作階段狀態模組實際上將建立一個名為 ASP.NET_SessionId 的 Cookie 並將會話 ID 儲存在其中。下面的虛擬碼顯示了建立 Cookie 的過程:

HttpCookie sessionCookie;sessionCookie = new HttpCookie("ASP.NET_SessionId", sessionID);sessionCookie.Path = "/";

工作階段 Cookie 的到期時間很短,在每個請求成功後更新到期時間。Cookie 的 Expires 屬性工作表示 Cookie 在用戶端的到期時間。如果未顯式設定工作階段 Cookie,Expires 屬性將預設為 DateTime.MinValue,即 .NET Framework 允許的最小時間單位。

要禁用工作階段 Cookie,請在設定檔中將 Cookieless 特性設定為 true,如下所示:

<configuration>    <system.web>        <sessionState Cookieless="true" />    </system.web></configuration>

此時,假設您請求以下 URL 處的頁面:

http://www.contoso.com/sample.aspx

瀏覽器地址欄中實際顯示的內容會有所不同,現在包含會話 ID,如下所示:

http://www.contoso.com/(5ylg0455mrvws1uz5mmaau45)/sample.aspx

執行個體化工作階段狀態 HTTP 模組時,該模組將檢查 Cookieless 特性的值。如果為 true,則將請求重新導向 (HTTP 302) 到經過修改的包含會話 ID(緊跟在頁面名稱前)的虛擬 URL。再次處理請求時,請求中會包含該會話 ID。如果請求啟動新的會話,HTTP 模組將產生新的會話 ID,然後重新導向該請求。如果回傳請求,則會話 ID 已經存在,因為回傳使用相對 URL。

使用 Cookieless 會話的缺點是,如果調用絕對 URL,將丟失工作階段狀態。使用 Cookie 時,您可以清除地址欄,轉至其他應用程式,然後返回上一個應用程式並檢索相同的會話值。如果在禁用工作階段 Cookie 時執行此操作,將丟失會話資料。例如,以下代碼將打斷該會話:

<a runat="server" href="/code/page.aspx">Click</a>

如果需要使用絕對 URL,請通過一些小技巧手動將會話 ID 添加到 URL 中。您可以對 HttpResponse 類調用 ApplyAppPathModifier 方法。

<a runat="server"     href=<% =Response.ApplyAppPathModifier("/code/page.aspx")%> >Click</a>

ApplyAppPathModifier 方法將使用表示 URL 的字串,並返回嵌入會話資訊的絕對 URL。例如,需要從 HTTP 頁面重新導向到 HTTPS 頁面時,此技巧特別有用。

小結

工作階段狀態最初由傳統的 ASP 引入,它是基於詞典的 API,使開發人員能夠儲存會話期間的自訂資料。在 ASP.NET 中,工作階段狀態支援以下兩種主要功能:Cookieless 會話 ID 儲存和傳輸,以及會話資料實際儲存的狀態供應器。為實現這兩種新功能,ASP.NET 利用 HTTP 模組控制工作階段狀態與正在處理的請求上下文之間的綁定。

在傳統的 ASP 中,使用工作階段狀態就是指使用 Cookie。在 ASP.NET 中已不再如此,因為可以使用 Cookieless 架構。藉助 HTTP 模組的力量,可以分解請求的 URL 以使其包含會話 ID,然後將其重新導向。接下來,HTTP 模組會從該 URL 中提取會話 ID 並使用它檢索任何儲存的狀態。

會話的物理狀態可以儲存在三個位置:進程內記憶體、進程外記憶體和 SQL Server 表。資料必須經過序列化/還原序列化處理,才能供應用程式使用。HTTP 模組會在請求開始時將會話值從提供者複製到應用程式的記憶體中。請求完成後,修改後的狀態將返回提供者。這種資料通訊會對效能產生不同程度的不利影響,但是會大大增強可靠性和穩定性,也使對 Web Farm 和 Web Garden 體繫結構的支援更容易實現。

作者簡介

Dino Esposito 是一位來自意大利羅馬的培訓教師和顧問。作為 Wintellect 小組的成員,Dino 專門研究 ASP.NET 和 ADO.NET,主要在歐洲和美國從事教學和諮詢工作。此外,Dino 還負責管理 Wintellect 的 ADO.NET 課件,並為 MSDN Magazine 的“Cutting Edge”專欄撰寫文章。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.