HTTP 協定本身無狀態,每次請求獨立。ASP.NET 提供多種客戶端狀態管理機制,各有優缺點與適用場景:
| 機制 | 儲存位置 | 容量限制 | 安全性 | 生命週期 | 適用場景 |
|---|---|---|---|---|---|
| Cookie | 客戶端硬碟/記憶體 | 4KB/個, 20-50個/域 | 可加密, 但可被竄改 | 可設定過期時間 | 使用者偏好、記住登入、追蹤 ID |
| QueryString | URL 參數 | ~2KB (IE 限制) | 明文, 易被竄改 | 單次請求 | 頁面間傳遞非敏感參數、分頁、搜尋條件 |
| Hidden Field | 表單隱藏欄位 | 無明確限制 | 明文, 可被檢視原始碼 | 單次 Postback | 頁面內暫存資料、表單驗證 Token |
| ViewState | 頁面隱藏欄位 (__VIEWSTATE) | 視頁面複雜度, 易膨脹 | 可啟用 MAC 驗證/加密 | 頁面生命週期 | 控制項狀態自動保持 (GridView 分頁、輸入值) |
| ControlState | 頁面隱藏欄位 | 極小 (僅必要資料) | 不可關閉, 內建保護 | 頁面生命週期 | 自訂控制項核心功能資料 (如 GridView 排序欄位) |
// 簡單寫入
Response.Cookies["UserName"].Value = "JohnDoe";
Response.Cookies["UserName"].Expires = DateTime.Now.AddDays(30);
// 安全寫入 (HttpOnly + Secure + SameSite)
var cookie = new HttpCookie("AuthToken", token) {
HttpOnly = true, // 防止 XSS 竊取
Secure = true, // 僅 HTTPS 傳輸
SameSite = SameSiteMode.Strict, // 防止 CSRF
Expires = DateTime.Now.AddHours(2)
};
Response.Cookies.Add(cookie);
if (Request.Cookies["UserName"] != null) {
string user = Request.Cookies["UserName"].Value;
lblWelcome.Text = $"歡迎回來, {HttpUtility.HtmlEncode(user)}!";
}
<!-- 頁面層級 -->
<%@ Page EnableViewState="false" %>
<!-- 控制項層級 -->
<asp:GridView ID="gvData" EnableViewState="true" />
<asp:Label ID="lblStatic" EnableViewState="false" />
public class CompressedViewStatePersister : PageStatePersister {
public CompressedViewStatePersister(Page page) : base(page) { }
public override void Load() {
var input = new MemoryStream(Convert.FromBase64String(
Page.Request.Form["__COMPRESSED_VIEWSTATE"]));
using (var gzip = new GZipStream(input, CompressionMode.Decompress))
using (var reader = new BinaryReader(gzip)) {
ViewState = reader.ReadObject();
ControlState = reader.ReadObject();
}
}
public override void Save() {
using (var output = new MemoryStream())
using (var gzip = new GZipStream(output, CompressionMode.Compress))
using (var writer = new BinaryWriter(gzip)) {
writer.WriteObject(ViewState);
writer.WriteObject(ControlState);
Page.ClientScript.RegisterHiddenField(
"__COMPRESSED_VIEWSTATE", Convert.ToBase64String(output.ToArray()));
}
}
}
// 在 Page 類別覆寫
protected override PageStatePersister PageStatePersister
=> new CompressedViewStatePersister(this);
public class PagerControl : WebControl {
private int _currentPage = 1;
protected override void OnInit(EventArgs e) {
base.OnInit(e);
Page.RegisterRequiresControlState(this);
}
protected override void LoadControlState(object savedState) {
_currentPage = (int)savedState;
}
protected override object SaveControlState() {
return _currentPage;
}
}
<pages enableViewStateMac="true"> (預設開啟)<pages viewStateEncryptionMode="Auto"> (需機器金鑰一致)SameSite=Strict/Lax 防禦 CSRF需要跨頁面/跨工作階段保持?
├─ 是 → 資料敏感?
│ ├─ 是 → 伺服端 Session/資料庫 + 客戶端只存 SessionID
│ └─ 否 → Cookie (設定適當過期時間)
└─ 單一頁面/Postback 內?
├─ 控制項自動狀態 → ViewState (預設)
├─ 自訂控制項核心邏輯 → ControlState
├─ 表單暫存資料 → Hidden Field
└─ 導航/分頁參數 → QueryString