網路延遲或伺服器回應緩慢時,使用者可能重複點擊提交按鈕,導致資料重複寫入、伺服器負載飆升。傳統做法在按鈕點擊後用 JavaScript 禁用按鈕,但無法防止使用者重新整理(F5)或直接重送 POST 請求。
public class NonReduplicatePostModule : IHttpModule
{
private static readonly ILog log = LogManager.GetLogger(typeof(NonReduplicatePostModule));
private const string HiddenFieldName = "__NonReduplicatePostModule__";
private const string MaskDivScriptUrl = "~/js/maskDiv.js";
private const string OnFormSubmitScript = "Evlon.MaskDiv.Instance.show();";
public void Init(HttpApplication context)
{
context.PreRequestHandlerExecute += OnPreRequestHandlerExecute;
}
public void Dispose() { }
private void OnPreRequestHandlerExecute(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
Page page = app.Context.Handler as Page;
if (page == null) return;
page.PreRender += (s, args) =>
{
// 註冊客戶端遮罩腳本
if (!page.ClientScript.IsClientScriptBlockRegistered("MaskDiv"))
{
page.ClientScript.RegisterClientScriptInclude("MaskDiv", page.ResolveUrl(MaskDivScriptUrl));
}
// 在表單提交時觸發遮罩
HtmlForm form = page.Form;
if (form != null) form.Attributes["onsubmit"] = OnFormSubmitScript;
};
// 檢查重複提交標記
string flag = app.Request.Form[HiddenFieldName];
if (!string.IsNullOrEmpty(flag))
{
if (app.Context.Cache[flag] != null)
{
log.Debug("Found reduplicate post");
// 切換到重複提交處理器
if (page is IReduplicatePostHandler)
app.Context.Handler = new ReduplicatePostHandler((IReduplicatePostHandler)page);
else
app.Context.Handler = new ReduplicatePostHandler();
}
else
{
// 首次提交,存入快取 1 分鐘
app.Context.Cache.Add(flag, DateTime.Now, null,
Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(1),
CacheItemPriority.Normal, null);
}
}
}
}
<system.web>
<httpModules>
<add name="NonReduplicatePostModule" type="Tests.NonReduplicatePostModule, TestAssembly" />
</httpModules>
</system.web>
<!-- IIS 7+ 整合模式 -->
<system.webServer>
<modules>
<add name="NonReduplicatePostModule" type="Tests.NonReduplicatePostModule, TestAssembly" />
</modules>
</system.webServer>
public interface IReduplicatePostHandler
{
void HandleReduplicatePost(HttpContext context);
}
public class ReduplicatePostHandler : IHttpHandler
{
private readonly IReduplicatePostHandler _pageHandler;
public ReduplicatePostHandler(IReduplicatePostHandler handler = null)
{ _pageHandler = handler; }
public bool IsReusable => false;
public void ProcessRequest(HttpContext context)
{
if (_pageHandler != null)
_pageHandler.HandleReduplicatePost(context);
else
context.Response.Write("請勿重複提交表單,系統正在處理中...");
}
}
HttpModule 提供乾淨、可重用的重複提交防護方案,結合伺服器端快取驗證與客戶端 UI 遮罩,兼顧安全性與使用者體驗。