asp.net防止刷新重复提交数据解决方法的分析

作者:袖梨 2022-06-25

在Asp.net开发中,浏览器刷新重复触发事件,导致重复提交的问题,下面有几种解决方法,我针对适用情况和利弊做一个简单的分析。

方法一:检测数据表,看是否有相同的数据。这种方法对插入数据可能意义更大些,不过如何定义是相同的数据,是个麻烦的事。同时,如果不是插入数据,是删除呢,就不适用了。

方法二:提交后,转向一个过渡页面,然后再从过渡页面返回到当前的页面。这就要求在提交后,要把当前页面的URL地址作为参数传过去,同时不能保存缓存,否则“后退”带来不良后果。不过,如果是简单的删除,似乎转来转去太麻烦了。

方法三:用JavaScript捕捉F5事件。比如有如下代码:

 代码如下 复制代码

window.document.onkeydown = KeyStroke;
function KeyStroke()
  {
   var key = event.keyCode;
   event.srcElement.releaseCapture();
   if(key == 116)
   {
   event.keyCode=0;
   event.returnValue=false;
   }
  }

似乎不错,禁用了F5,不过如果浏览器禁用了js咋办,如果用右键来刷新咋办?禁用右键么?不能为了实现一个功能来禁用这禁用那的,不合情理。

方法四:基于session和ViewState来处理。最好的例子见:

这个方法的原理是”在页面代码执行的末尾将当前的ViewState写到一个Session中,而在页面加载时则判断该Session值是否与当前ViewState相等(其实Session值恰好是ViewState的前一状态),若不等,则是正常的postback,若是相等则是浏览器刷新”.这个方法还不错,不过对session占用如此多的服务器资源,我有些担心。

代码如下

SubmitOncePage

    SubmitOncePage是针对上述分析写的一个继承自System.Web.UI.Page的基类,需要防止刷新重复提交数据的页面从该基类继承,源码如下:

 

 代码如下 复制代码

namespace myControl
{
    /**////


    /// 名称:SubmitOncePage
    /// 父类:System.Web.UI.Page
    /// 描述:解决浏览器刷新造成的数据重复提交问题的page扩展类。
    /// 示例:    if (!this.IsRefreshed)
    ///            {
    ///                //具体代码
    ///            }
    ///    原创:丛兴滋(cncxz)    E-mail:[email protected]
    ///

    public class SubmitOncePage:System.Web.UI.Page
        {
            private string _strSessionKey;
            private string _hiddenfieldName;
            private string _strLastViewstate;
          
            public SubmitOncePage()
            {
                _hiddenfieldName = "__LastVIEWSTATE_SessionKey";
                _strSessionKey = System.Guid.NewGuid().ToString();
                _strLastViewstate = string.Empty;
            }

            public bool IsRefreshed
            {
                get
                {
                    string str1 = GetSessinContent();
                    _strLastViewstate = str1;
                    string str2 = this.Session[GetSessinKey()] as string;
                    bool flag1 = (str1 != null) && (str2 != null) && (str1 == str2);
                    return flag1;
                }
            }

            protected override void Render(System.Web.UI.HtmlTextWriter writer)
            {
                string str = GetSessinKey();
                this.Session[str] = _strLastViewstate;
                this.RegisterHiddenField(_hiddenfieldName, str);
                base.Render(writer);
            }


            private string GetSessinKey()
            {
                string str = this.Request.Form[_hiddenfieldName];
                return (str == null) ? _strSessionKey : str;
            }

            private string GetSessinContent() {
                string str = this.Request.Form["__VIEWSTATE"];
                if (str == null) {
                    return null;
                }
                return System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(str, "MD5");
            }
          
        }
}

测试项目

    首先将SubmitOncePage类的源码编译成一个单独的dll,然后进行测试,步骤如下:

    1、新建一个asp.net web应用程序;
    2、添加SubmitOncePage类对应的dll引用;
    3、给webform1添加一个Label控件(Label1)和一个Button控件(Button1);
    4、设置Label1的Text为0;
    5、双击Button1转到codebehind视图;
    6、修改类WebForm1的父类为SubmitOncePage并添加测试代码,结果如下:

 代码如下 复制代码

public class WebForm1 : myControl.SubmitOncePage
    {
        protected System.Web.UI.WebControls.Label Label1;
        protected System.Web.UI.WebControls.Button Button1;   
       

        Web 窗体设计器生成的代码#region Web 窗体设计器生成的代码
        override protected void OnInit(EventArgs e)
        {
            //
            // CODEGEN: 该调用是 ASP.NET Web 窗体设计器所必需的。
            //
            InitializeComponent();
            base.OnInit(e);
        }
       
        /**////


        /// 设计器支持所需的方法 - 不要使用代码编辑器修改
        /// 此方法的内容。
        ///

        private void InitializeComponent()
        {   
            this.Button1.Click += new System.EventHandler(this.Button1_Click);
        }
        #endregion

        private void Button1_Click(object sender, System.EventArgs e)
        {
            int i=int.Parse(Label1.Text)+1;
            Label1.Text = i.ToString();
            if (!this.IsRefreshed)
            {
                WriteFile("a.txt", i.ToString());
            }
            WriteFile("b.txt", i.ToString()); 
       
           
        }

        private void WriteFile(string strFileName,string strContent)
        {
            string str = this.Server.MapPath(strFileName);      
            System.IO.StreamWriter sw = System.IO.File.AppendText(str);
            sw.WriteLine(strContent);
            sw.Flush();
            sw.Close(); 
        }
    }

    7、按F5运行,在浏览器窗口中连续点击几次Button1,然后刷新几次页面,再点击几次Button1;
    8、转到测试项目对应目录下,打开a.txt和b.txt文件,可看到if (!this.IsRefreshed) 的具体效果


方法五:用ajax提交操作。当然,这是从源头上就没有刷新的问题,因为postback都没有了,不存在防止刷新提交问题。有些偏题。

方法六:通过 HttpContext 对象传递给页面处理。这个方法是msdn给的一个方法,原理我就不赘述了,详见http://msdn.microsoft.com/zh-cn/library/ms379557(VS.80).aspx。我觉得这个方法不错,根据asp.net的运行机制来处理此问题,就比较游刃有余了。所以这个方法我推荐使用,虽然是msdn的这篇文章是2004年的老文了,现在看来依然很不错。

相关文章

精彩推荐