ViewState 是 Web Forms 區別於傳統 ASP 的關鍵特性:**自動保持控制項狀態跨 PostBack 不遺失**,解決經典 ASP 表單提交後值被清空的痛點。本文深入解析 ViewState 運作原理、層級控制、效能影響、安全性考量與最佳實務。
<form action="classic.asp" method="post">
姓名:<input type="text" name="fname" size="20">
<input type="submit" value="送出">
</form>
<%
fname = Request.Form("fname")
If fname <> "" Then Response.Write("Hello " & fname & "!")
%>
提交後若伺服器驗證失敗回傳,**所有輸入值消失**,使用者必須重新填寫。
<form runat="server">
姓名:<asp:TextBox ID="txtName" runat="server" />
<asp:Button Text="送出" runat="server" OnClick="btn_Click" />
<asp:Label ID="lblMsg" runat="server" />
</form>
<script runat="server">
void btn_Click(object sender, EventArgs e) {
lblMsg.Text = "Hello " + txtName.Text + "!";
}
</script>
提交後**文字方塊值保留**,Label 顯示結果,使用者體驗大幅提升。
每個 <form runat="server"> 自動包含隱藏欄位:
<input type="hidden" name="__VIEWSTATE"
value="dDwtNTI0ODU5MDE1Ozs+ZBCF2ryjMpeVgUrY2eTj79HNl4Q=" />
SaveViewState 階段,將所有啟用 ViewState 控制項的屬性值序列化為 Base64 字串LoadViewState 階段還原控制項屬性值Init - 控制項樹建立LoadViewState - **還原 ViewState 資料**(PostBack 時)LoadPostData - 處理表單輸入值(IPostBackDataHandler)Load - Page_Load 事件RaisePostBackEvent - 按鈕 Click 等事件SaveViewState - **序列化儲存 ViewState**Render - 產生 HTML 輸出<%@ Page EnableViewState="false" %>
整頁所有控制項停用 ViewState(除非控制項明確 EnableViewState="true")。
<asp:GridView ID="gvData" runat="server" EnableViewState="false" /> <-- 僅此控制項關閉
<asp:TextBox ID="txtInput" runat="server" EnableViewState="true" /> <-- 強制啟用
控制項繼承 Control 類別,可覆寫 SaveViewState/LoadViewState 自訂特定屬性是否納入 ViewState。
| 類別 | 範例 | 是否預設儲存 |
|---|---|---|
| 控制項屬性值 | TextBox.Text, Label.Text, DropDownList.SelectedIndex, GridView.PageIndex | 是 |
| 樣式屬性 | ForeColor, BackColor, Font, CssClass | 是 |
| 資料繫結結果 | GridView/DataList/Repeater 繫結後的資料列集合 | 是(佔用最大) |
| 動態建立控制項 | 程式碼中 new 的控制項屬性 | 是(需每次 PostBack 重建) |
| ViewState["key"] | 開發者自訂儲存的物件 | 是 |
| 表單輸入值 | TextBox 輸入、CheckBox 勾選 | **否**(由 LoadPostData 處理) |
過大 ViewState 導致:頻寬浪費、回傳延遲、序列化/反序列化 CPU 開銷、最大要求長度限制(預設 4MB)。
EnableViewState="false" 針對唯讀顯示控制項(Label、Literal、Repeater)AllowPaging="true" + PageSize 限制單頁筆數SavePageStateToPersistenceMedium/LoadPageStateFromPersistenceMedium 實作 GZip 壓縮<pages viewStateEncryptionMode="Auto" maxPageStateFieldLength="1024" /> 分割隱藏欄位protected override void SavePageStateToPersistenceMedium(object viewState) {
using (MemoryStream ms = new MemoryStream()) {
using (GZipStream gzip = new GZipStream(ms, CompressionMode.Compress)) {
LosFormatter formatter = new LosFormatter();
formatter.Serialize(gzip, viewState);
}
ClientScript.RegisterHiddenField("__VIEWSTATE", Convert.ToBase64String(ms.ToArray()));
}
}
protected override object LoadPageStateFromPersistenceMedium() {
string vs = Request.Form["__VIEWSTATE"];
if (string.IsNullOrEmpty(vs)) return null;
byte[] bytes = Convert.FromBase64String(vs);
using (MemoryStream ms = new MemoryStream(bytes)) {
using (GZipStream gzip = new GZipStream(ms, CompressionMode.Decompress)) {
LosFormatter formatter = new LosFormatter();
return formatter.Deserialize(gzip);
}
}
}
Base64 非加密,任何人可解碼查看內容結構。敏感資料**絕不可**存入 ViewState。
<%@ Page EnableViewStateMac="true" %> <-- 預設開啟
<pages enableViewStateMac="true" viewStateEncryptionMode="Auto" /> <-- web.config
EnableViewStateMac:附加雜湊碼驗證完整性,防止惡意竄改viewStateEncryptionMode="Auto":含 ViewStateMode="Encrypted" 控制項時自動加密<machineKey validationKey="AutoGenerate,IsolateApps"
decryptionKey="AutoGenerate,IsolateApps"
validation="SHA1" decryption="AES" />
Web Farm 環境必須手動設定相同 machineKey 確保跨伺服器驗證一致。
| 特性 | ViewState | Session | Cache | ControlState |
|---|---|---|---|---|
| 儲存位置 | 客戶端(隱藏欄位) | 伺服器端記憶體 | 伺服器端記憶體 | 客戶端(隱藏欄位) |
| 生命週期 | 單一頁面 PostBack | 使用者 Session 期間 | 應用程式存活期間 | 單一頁面 PostBack |
| 容量限制 | 受限於頻寬/要求大小 | 記憶體/資料庫 | 記憶體 | 極小(關鍵狀態) |
| 安全性 | 可被解碼/竄改 | 伺服器端較安全 | 伺服器端 | 不可關閉、受 MAC 保護 |
| 適用資料 | UI 狀態、分頁索引、排序 | 使用者資料、購物車 | 共用資料、查詢結果 | 控制項核心運作狀態 |
| 可關閉 | 是 | N/A | N/A | 否 |
自訂控制項開發時,核心運作狀態(如 GridView 分頁索引、排序運算式)應儲存於 ControlState,確保即使頁面關閉 ViewState 仍能正常運作:
protected override void OnInit(EventArgs e) {
Page.RegisterRequiresControlState(this);
base.OnInit(e);
}
protected override object SaveControlState() {
return new Pair(base.SaveControlState(), _currentPageIndex);
}
protected override void LoadControlState(object savedState) {
Pair p = (Pair)savedState;
base.LoadControlState(p.First);
_currentPageIndex = (int)p.Second;
}
EnableViewStatePageSizeEnableViewStateMac="true"、viewStateEncryptionMode="Auto" 啟用machineKeyViewState 是 Web Forms 狀態管理的基石,理解其運作機制、效能代價與安全性風險,能建構出高效能、安全的 Web 應用程式。下一節將介紹 Web Forms 表單架構與 runat="server" 核心機制。