首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> ASP.NET 基礎

ASP.NET 防止頁面重新整理導致資料重複提交的兩種解決方案

     編輯:ASP.NET 基礎

方案一:HTTP 快取控制標頭(簡易但不完全可靠)

Response.Buffer = true;
Response.ExpiresAbsolute = DateTime.Now.AddSeconds(-1);
Response.Expires = 0;
Response.CacheControl = "no-cache";

原理:讓瀏覽器認為頁面已過期,使用者按「上一頁」或重整時會提示「網頁已過期」。缺點:現代瀏覽器行為不一,無法徹底阻止重複 POST,僅作輔助。

方案二:Post-Redirect-Get (PRG) 模式(推薦標準做法)

  1. 使用者 POST 表單。
  2. 伺服器處理完成(寫入 DB)後,回傳 302 Redirect 導向同一頁面(或結果頁)的 GET 請求。
  3. 瀏覽器記錄中只剩 GET,重整只會重發 GET,不會重複提交資料。
// 處理完新增邏輯後
Response.Redirect(Request.RawUrl); // 或 Response.Redirect("Success.aspx");

方案三:ViewState + Session 唯一 Token 機制(本文核心:SubmitOncePage 基底類別)

適用於**必須保留頁面狀態(如驗證失誤需回填表單)無法使用 PRG** 的情境。

核心邏輯

  1. 頁面初始化產生唯一 SessionKey (Guid),寫入隱藏欄位 __LastVIEWSTATE_SessionKey。
  2. 預先計算目前 ViewState 的 MD5 雜湊,存入 Session[SessionKey]。
  3. PostBack 時比對:
    - 若 Session[SessionKey] == 目前 ViewState MD5 → 判定為**重新整理**(ViewState 未變)。
    - 否則 → 正常 PostBack,更新 Session 為新 ViewState MD5。

SubmitOncePage 完整程式碼

public class SubmitOncePage : System.Web.UI.Page
{
    private string _sessionKey;
    private const string HiddenFieldName = "__LastVIEWSTATE_SessionKey";
    private string _lastViewStateHash;

    public SubmitOncePage()
    {
        _sessionKey = Guid.NewGuid().ToString();
    }

    public bool IsRefreshed
    {
        get
        {
            string currentHash = GetViewStateHash();
            _lastViewStateHash = currentHash;
            string storedHash = Session[GetSessionKey()] as string;
            return !string.IsNullOrEmpty(currentHash) && 
                   !string.IsNullOrEmpty(storedHash) && 
                   currentHash == storedHash;
        }
    }

    protected override void Render(HtmlTextWriter writer)
    {
        // 頁面輸出前,將「上一次」的 ViewState Hash 存入 Session
        Session[GetSessionKey()] = _lastViewStateHash;
        // 將 SessionKey 寫入隱藏欄位,下次 PostBack 時帶回
        ClientScript.RegisterHiddenField(HiddenFieldName, GetSessionKey());
        base.Render(writer);
    }

    private string GetSessionKey()
    {
        return Request.Form[HiddenFieldName] ?? _sessionKey;
    }

    private string GetViewStateHash()
    {
        string viewState = Request.Form["__VIEWSTATE"];
        if (string.IsNullOrEmpty(viewState)) return null;
        return FormsAuthentication.HashPasswordForStoringInConfigFile(viewState, "MD5");
    }
}

繼承使用範例

public partial class WebForm1 : SubmitOncePage
{
    protected void Button1_Click(object sender, EventArgs e)
    {
        if (!this.IsRefreshed)
        {
            // 真正的業務邏輯:寫入 DB、發送 Email 等
            InsertData();
        }
        // 重新整理時這裡不會執行,但頁面仍正常渲染
    }
}

方案比較表

方案優點缺點適用場景
Cache-Control零程式碼修改不可靠、瀏覽器相容性差輔助用
PRG (Redirect)標準、無 Session 消耗、支援瀏覽器歷史無法保留表單輸入值(驗證失敗時)大多數新增/編輯頁面
ViewState Token保留頁面狀態、無需 Redirect消耗 Session、增加 ViewState 大小、需繼承基底類別複雜表單、驗證失敗需回填

注意事項

  • Session 模式若為 InProc,重啟 IIS 會導致 Token 遺失;建議搭配 StateServer/SQL Server/Redis。
  • 大量並發頁面會產生大量 Session Key,需定期清理或設定較短 Timeout。
  • 若專案已升級至 ASP.NET Core,建議使用 AntiforgeryToken + PRG 組合,或 Idempotency Key 模式。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved