使用HttpModule来禁用Web表单重复提交
在网速慢或者网站反应慢的情况下,如果提交表单需要超过5秒的时间还未提交成功,多数人会重新点击提交。这样不仅造成错误的数据,还会加剧服务器的压力。
通过使用HttpModule,我们可以在表单处理前,检测一些标志,从而防止用户重复提交数据,再通过一些接口,让用户自己来处理重复提交时,应该如何告诉用户。
通过使用HttpModule,我们也可以在客户端表单提交时,使用DIV覆盖住表单,从UI层防止用户再次单击提交(用户直接F5管不了)。
这种方法使用简单,直接把脚本和图片放在指定的目录中,然后在Web.config中添加Module
<httpModules>
<!--防止重复提交 LOG记录MODULE -->
<add name="NonReduplicatePostModule" type="tests.NonReduplicatePostModule,test"/>
</httpModules>
下面是实现代码:
/// NonReduplicatePostModule 的摘要说明。
/// </summary>
public class NonReduplicatePostModule : System.Web.IHttpModule
{
private static ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private const string hiddenFileName = "__NonReduplicatePostModule__";
private const string maskdivScriptRelativeUrl = "~/js/maskDiv.js";
private const string onformSubmit = "Evlon.MaskDiv.Instance.show();";
private HttpApplication context = null;
#region IHttpModule 成员
public void Init(HttpApplication context)
{
this.context = context;
this.context.PreRequestHandlerExecute+=new EventHandler(context_PreRequestHandlerExecute);
}
public void Dispose()
{
this.context.PreRequestHandlerExecute-=new EventHandler(context_PreRequestHandlerExecute);
}
#endregion
private void context_PreRequestHandlerExecute(object sender, EventArgs e)
{
HttpApplication webApp = sender as HttpApplication;
if(webApp != null)
{
//已经处理过,提示用户不要重复提交
Page page = webApp.Context.Handler as Page;
if(page != null)
{
page.PreRender+=new EventHandler(page_PreRender);
//找到Page,添加时间
if(webApp.Request.Form[hiddenFileName] != null)
{
string flag = webApp.Request.Form[hiddenFileName].ToString();
if(webApp.Context.Cache.Get(flag) != null)
{
log.Debug("found reduplicate post");
if(page is IReduplicatePostHandler)
{
webApp.Context.Handler = new ReduplicatePostHandler((IReduplicatePostHandler)page);
}
else
{
webApp.Context.Handler = new ReduplicatePostHandler();
}
}
else
{
//放进缓存中,表示已经被处理过,在一分钟后自动移聊(可再次提交)
webApp.Context.Cache.Add(flag,DateTime.Now,null,System.Web.Caching.Cache.NoAbsoluteExpiration,TimeSpan.FromMinutes(1),System.Web.Caching.CacheItemPriority.Normal,null);
}
}
}
}
}
private void page_PreRender(object sender, EventArgs e)
{
Page page = sender as Page;
if(page != null)
{
//找到Page,添加时间
page.RegisterHiddenField(hiddenFileName,string.Format("{0}_{1}_{2}",page.Session.SessionID.GetHashCode(), page.GetType().GetHashCode(), DateTime.Now.Ticks));
//表单UI显示 MASKDIV
page.RegisterClientScriptBlock("maskdiv_include","<script type='text/javascript' src='http://www.aspcool.com/lanmu/" + page.ResolveUrl(maskdivScriptRelativeUrl) + "' ></script>");
page.RegisterOnSubmitStatement("maskdiv", onformSubmit);
}
}
}
public interface IReduplicatePostHandler
{
void OnReduplicatePost(HttpContext context, EventArgs e);
}
internal class ReduplicatePostHandler : IHttpHandler
{
private IReduplicatePostHandler handler = null;
internal ReduplicatePostHandler(IReduplicatePostHandler handler)
{
this.handler = handler;
}
internal ReduplicatePostHandler()
{
}
#region IHttpHandler 成员
public void ProcessRequest(HttpContext context)
{
if(handler != null)
{
handler.OnReduplicatePost(context,new EventArgs());
}
else
{
context.Response.Write("不要重复提交");
}
}
public bool IsReusable
{
get
{
// TODO: 添加 ReduplicatePostHandler.IsReusable getter 实现
return false;
}
}
#endregion
}
用到的JS文件:/js/MaskDIV.js
Evlon = {};
Evlon.MaskDiv = function()
{
var div = window.document.createElement("DIV");
div.style.position = "absolute";
div.style.top = "0px";
div.style.left = "0px";
div.style.width = '100%';//document.body.scrollWidth + 'px';
div.style.height = '100%'; //document.body.scrollHeight + 'px';
div.style.backgroundColor = "white";
div.style.zIndex = 999;
div.style.filter = "Alpha(style=0,opacity=50)";
div.style.opacity="0.50";
this.divMask = div;
div = window.document.createElement("DIV");
div.style.position = "absolute";
div.style.top = "0px";
div.style.left = "0px";
div.style.width = '100%';//document.body.scrollWidth + 'px';
div.style.height = '100%'; //document.body.scrollHeight + 'px';
div.style.zIndex = 1000;
this.divTooltip = div;
this.show = function()
{
//创建半透明DIV
window.__maskDiv__ = document.body.insertAdjacentElement('afterBegin',this.divMask);
//创建提示DIV
window.__maskDivText__ = document.body.insertAdjacentElement('afterBegin',this.divTooltip);
window.__maskDivText__.innerHTML = "<table style='border-collapse:collapse;border-width:0px;width:100%;height:100%;'0><tr height='38%'><td></td></tr><tr><td align='center' valign='top'>" +
"<table style='border-collapse:collapse;border:solid 1px yellow;'><tr><td align='right'><img width='20' height='20' src='http://www.aspcool.com/lanmu/image/loading.gif' usesrc='http://www.aspcool.com/lanmu/image/loading.gif' onerror='this.src=http://www.aspcool.com/lanmu/this.usesrc = \"../\" + this.usesrc'/></td>" +
"<td align='left'><span style = 'filter:alpha(opacity=100);background-color:#ccc;font-size:20px'>提交中.</span></td></tr></table>" +
"</td></tr></table>";
}
this.hide = function()
{
if(window.__maskDiv__ != null)
{
document.body.removeChild(window.__maskDiv__);
window.__maskDiv__ = null;
}
if(window.__maskDivText__ != null)
{
window.__maskDivText__.innerHTML = '';
document.body.removeChild(window.__maskDivText__);
window.__maskDivText__ = null;
}
}
}
Evlon.MaskDiv.Instance = new Evlon.MaskDiv();
window.showModalDialogMask = function(sURL , vArguments, sFeatures)
{
var sign = window.showModalDialog(sURL , vArguments, sFeatures);
if(sign != "" && sign != null)
{
//创建半透明DIV
Evlon.MaskDiv.Instance.show();
}
return null;
//return sign;
}
通过使用HttpModule,我们可以在表单处理前,检测一些标志,从而防止用户重复提交数据,再通过一些接口,让用户自己来处理重复提交时,应该如何告诉用户。
通过使用HttpModule,我们也可以在客户端表单提交时,使用DIV覆盖住表单,从UI层防止用户再次单击提交(用户直接F5管不了)。
这种方法使用简单,直接把脚本和图片放在指定的目录中,然后在Web.config中添加Module
<httpModules>
<!--防止重复提交 LOG记录MODULE -->
<add name="NonReduplicatePostModule" type="tests.NonReduplicatePostModule,test"/>
</httpModules>
下面是实现代码:
/// NonReduplicatePostModule 的摘要说明。
/// </summary>
public class NonReduplicatePostModule : System.Web.IHttpModule
{
private static ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private const string hiddenFileName = "__NonReduplicatePostModule__";
private const string maskdivScriptRelativeUrl = "~/js/maskDiv.js";
private const string onformSubmit = "Evlon.MaskDiv.Instance.show();";
private HttpApplication context = null;
#region IHttpModule 成员
public void Init(HttpApplication context)
{
this.context = context;
this.context.PreRequestHandlerExecute+=new EventHandler(context_PreRequestHandlerExecute);
}
public void Dispose()
{
this.context.PreRequestHandlerExecute-=new EventHandler(context_PreRequestHandlerExecute);
}
#endregion
private void context_PreRequestHandlerExecute(object sender, EventArgs e)
{
HttpApplication webApp = sender as HttpApplication;
if(webApp != null)
{
//已经处理过,提示用户不要重复提交
Page page = webApp.Context.Handler as Page;
if(page != null)
{
page.PreRender+=new EventHandler(page_PreRender);
//找到Page,添加时间
if(webApp.Request.Form[hiddenFileName] != null)
{
string flag = webApp.Request.Form[hiddenFileName].ToString();
if(webApp.Context.Cache.Get(flag) != null)
{
log.Debug("found reduplicate post");
if(page is IReduplicatePostHandler)
{
webApp.Context.Handler = new ReduplicatePostHandler((IReduplicatePostHandler)page);
}
else
{
webApp.Context.Handler = new ReduplicatePostHandler();
}
}
else
{
//放进缓存中,表示已经被处理过,在一分钟后自动移聊(可再次提交)
webApp.Context.Cache.Add(flag,DateTime.Now,null,System.Web.Caching.Cache.NoAbsoluteExpiration,TimeSpan.FromMinutes(1),System.Web.Caching.CacheItemPriority.Normal,null);
}
}
}
}
}
private void page_PreRender(object sender, EventArgs e)
{
Page page = sender as Page;
if(page != null)
{
//找到Page,添加时间
page.RegisterHiddenField(hiddenFileName,string.Format("{0}_{1}_{2}",page.Session.SessionID.GetHashCode(), page.GetType().GetHashCode(), DateTime.Now.Ticks));
//表单UI显示 MASKDIV
page.RegisterClientScriptBlock("maskdiv_include","<script type='text/javascript' src='http://www.aspcool.com/lanmu/" + page.ResolveUrl(maskdivScriptRelativeUrl) + "' ></script>");
page.RegisterOnSubmitStatement("maskdiv", onformSubmit);
}
}
}
public interface IReduplicatePostHandler
{
void OnReduplicatePost(HttpContext context, EventArgs e);
}
internal class ReduplicatePostHandler : IHttpHandler
{
private IReduplicatePostHandler handler = null;
internal ReduplicatePostHandler(IReduplicatePostHandler handler)
{
this.handler = handler;
}
internal ReduplicatePostHandler()
{
}
#region IHttpHandler 成员
public void ProcessRequest(HttpContext context)
{
if(handler != null)
{
handler.OnReduplicatePost(context,new EventArgs());
}
else
{
context.Response.Write("不要重复提交");
}
}
public bool IsReusable
{
get
{
// TODO: 添加 ReduplicatePostHandler.IsReusable getter 实现
return false;
}
}
#endregion
}
用到的JS文件:/js/MaskDIV.js
Evlon = {};
Evlon.MaskDiv = function()
{
var div = window.document.createElement("DIV");
div.style.position = "absolute";
div.style.top = "0px";
div.style.left = "0px";
div.style.width = '100%';//document.body.scrollWidth + 'px';
div.style.height = '100%'; //document.body.scrollHeight + 'px';
div.style.backgroundColor = "white";
div.style.zIndex = 999;
div.style.filter = "Alpha(style=0,opacity=50)";
div.style.opacity="0.50";
this.divMask = div;
div = window.document.createElement("DIV");
div.style.position = "absolute";
div.style.top = "0px";
div.style.left = "0px";
div.style.width = '100%';//document.body.scrollWidth + 'px';
div.style.height = '100%'; //document.body.scrollHeight + 'px';
div.style.zIndex = 1000;
this.divTooltip = div;
this.show = function()
{
//创建半透明DIV
window.__maskDiv__ = document.body.insertAdjacentElement('afterBegin',this.divMask);
//创建提示DIV
window.__maskDivText__ = document.body.insertAdjacentElement('afterBegin',this.divTooltip);
window.__maskDivText__.innerHTML = "<table style='border-collapse:collapse;border-width:0px;width:100%;height:100%;'0><tr height='38%'><td></td></tr><tr><td align='center' valign='top'>" +
"<table style='border-collapse:collapse;border:solid 1px yellow;'><tr><td align='right'><img width='20' height='20' src='http://www.aspcool.com/lanmu/image/loading.gif' usesrc='http://www.aspcool.com/lanmu/image/loading.gif' onerror='this.src=http://www.aspcool.com/lanmu/this.usesrc = \"../\" + this.usesrc'/></td>" +
"<td align='left'><span style = 'filter:alpha(opacity=100);background-color:#ccc;font-size:20px'>提交中.</span></td></tr></table>" +
"</td></tr></table>";
}
this.hide = function()
{
if(window.__maskDiv__ != null)
{
document.body.removeChild(window.__maskDiv__);
window.__maskDiv__ = null;
}
if(window.__maskDivText__ != null)
{
window.__maskDivText__.innerHTML = '';
document.body.removeChild(window.__maskDivText__);
window.__maskDivText__ = null;
}
}
}
Evlon.MaskDiv.Instance = new Evlon.MaskDiv();
window.showModalDialogMask = function(sURL , vArguments, sFeatures)
{
var sign = window.showModalDialog(sURL , vArguments, sFeatures);
if(sign != "" && sign != null)
{
//创建半透明DIV
Evlon.MaskDiv.Instance.show();
}
return null;
//return sign;
}