簡介
在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。)此物件儲存以名稱/值對集合的方式組織的資料。 「名稱」佔位符表示用來檢索資訊的關鍵字,而「值」佔位符表示會話狀態中儲存的內容。名稱/值對按照會話ID 進行分組,這樣,每個使用者看到的只是他/她自己建立的名稱/值對。
在ASP.NET 中,會話狀態的程式介面與傳統的ASP 幾乎是相同的。但它們的基礎實作是完全不同的,前者比後者更具靈活性、可擴展性和更強的程式功能。在深入研究ASP.NET 會話狀態之前,讓我們先簡單回顧一下ASP.NET 會話基礎架構的某些結構功能。
在ASP.NET 中,任何傳入HTTP 請求都要透過HTTP 模組管道進行傳輸。每個模組都可以篩選並修改請求所攜帶的大量資訊。與每個請求關聯的資訊叫做“呼叫上下文”,程式設計中用HttpContext 物件來表示。我們不應將請求的上下文視為狀態資訊的另一個容器,雖然它提供的Items 集合只是一個資料容器。 HttpContext 物件不同於所有其他狀態物件(例如,Session、Application 和Cache),因為它的有限生命週期超出了處理請求所需的時間。當請求通過一系列註冊的HTTP 模組後,其HttpContext 物件將包含對狀態物件的參考。當最終可以處理請求時,關聯的呼叫上下文將綁定到特定會話(Session) 和全域狀態物件(Application 和Cache)。
負責設定每個使用者的會話狀態的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.exe 或w3wp.exe)的記憶體中保持為活動物件。這是預設選項。
StateServer 會話值被序列化並儲存在單獨進程(aspnet_state.exe) 的記憶體中。該進程還可以在其他電腦上運行。
SQLServer 會話值被序列化並儲存在Microsoft? SQL Server? 表中。 SQL Server 的執行個體可以在本機上運行,也可以遠端執行。
會話狀態HTTP 模組將從web.config 檔案的<sessionState>; 部分讀取目前選定的狀態提供者。
<sessionState mode="InProc | StateServer | SQLServer />;
根據mode 特性的值,將透過不同的步驟從不同的進程中檢索會話狀態並將其儲存到不同的進程中。預設情況下,會話狀態儲存在本機的ASP.NET 輔助進程中。
例如,名為aspnet_state.exe
的Windows NT 服務中)。
,使它們成為字典物件。
間的
綁定將持續到請求結束。
。
圖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:WINNTMicrosoft.NETFrameworkv1.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 特性指定連接字串。請注意,特性字串必須包含使用者ID、密碼和伺服器名稱。它不能包含Database 和Initial Catalog 之類的標記,因為此資訊預設為固定名稱。使用者ID 和密碼可以替換為整合的安全設定。
如何建立資料庫? ASP.NET 提供兩對腳本來設定資料庫環境。第一對腳本名為InstallSqlState.sql 和UninstallSqlState.sql,與會話狀態NT 服務位於同一個資料夾中。它們會建立名為ASPState 的資料庫和幾個儲存的過程。但是,資料儲存在SQL Server 暫存區域TempDB 資料庫中。這意味著,如果重新啟動SQL Server 計算機,會話資料將會遺失。
要解決這一局限性,請使用第二對腳本。第二對腳本名為InstallPersistSqlState.sql 和UninstallPersistSqlState.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:
瀏覽器網址列中實際顯示的內容會有所不同,現在包含會話ID,如下所示:
http:(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 體系結構的支援更容易實現。