ASP.NET中防止刷新页面造成表单重复提交

时间:2022-09-11 23:24:57
     在Web开发中,必须面对的问题就是表单的重复提交问题(这里仅指F5刷新造成的重复提交),.NET中处理这个问题似乎没有什么好的方法。在网上搜索得到的解决方法主要有两种,一种是直接让表单按钮失效,从而保证一个用户对于一个表单只能提交一次;另一种方法,是一次提交后把表单清空,在后台逻辑上进行判断,从而区分是否重复提交。
    个人感觉,第一种方法,用户体验不好,按钮只能按一次,这种应该是用来防治多次点击提交按钮造成的重复提交的,无法防止刷新造成的二次提交;第二种,两种重复提交都可以防止,但是表单内容就没了,万一有需求不让内容消失,就费些周折。
    于是,自己模仿JSP中Struts的令牌,写了一个防止表单被重复提交的方法,和大家分享。

实现原理:
   
由于刷新提交表单,实际上提交的就是上一次正常提交的表单,所以我们只要做一个标志,判断出是新表单还是上一次的旧表单就可以分辨出是否进行了重复提交操作。

实现方法:
   在页面上放置一个Hidden域,当页面第一次载入的时候,在Session里面保存一个标志,同时,把这个标志保存到页面上的Hidden里面。在提交表单时,判断表单中提交上来的Hidden和Session中的标志是否一致,就可以知道是正常的提交表单,还是刷新页面导致的重复提交。需要注意的是,在每次提交表单的处理之后,要更新Session里面的标志。

代码实例:
    代码很少,首先是页面上。
  1. <html xmlns="http://www.w3.org/1999/xhtml" >
  2. <head runat="server">
  3.     <title></title>
  4. </head>
  5. <body>
  6.     <form id="form1" runat="server">
  7.         <div>
  8.             <input type="text" id="tbxName" runat="server"/>
  9.             <input type="text" id="tbxPass" value=""  runat="server"/>
  10.             <asp:Button ID="btnSubmit" runat="server" OnClick="Button1_Click" Text="Button" />
  11.             <asp:Label ID="lblMessage" runat="server" Text=""></asp:Label>
  12.             <input id="hiddenTest" type="hidden" value="<%= GetToken() %>" name="hiddenTestN"/>
  13.         </div>
  14.     </form>
  15. </body>
  16. </html>
    需要注意的地方:
    1
GetSessionToken()函数是为了获得Session里面保存的标志。
   2Hidden使用了非服务器控件,这是因为我使用服务器控件,并在后台直接获取Session的标志并赋值给这个Hidden的时候,刷新提交到服务器的表单中的Hidden的值也发生了改变,猜想是服务器控件的话,表单里面的值是保持同步的,当然,也可能是我用的方法不对,嘎嘎。

    下面是后台代码:
  1. using System;
  2. using System.Data;
  3. using System.Configuration;
  4. using System.Web;
  5. using System.Web.Security;
  6. using System.Web.UI;
  7. using System.Web.UI.WebControls;
  8. using System.Web.UI.WebControls.WebParts;
  9. using System.Web.UI.HtmlControls;
  10. using System.Security.Cryptography;
  11. using System.Text;

  12. public partial class _Default : System.Web.UI.Page 
  13. {
  14.     protected void Page_Load(object sender, EventArgs e)
  15.     {
  16.         //第一次载入的时候,生成一个初始的标志
  17.         if (null == Session["Token"])
  18.         {
  19.             SetToken();
  20.         }
  21.     }

  22.     protected void Button1_Click(object sender, EventArgs e)
  23.     {
  24.         if (Request.Form.Get("hiddenTestN").Equals(GetToken()))
  25.         {
  26.             lblMessage.ForeColor = System.Drawing.Color.Blue;
  27.             lblMessage.Text = "正常提交表单";
  28.             SetToken();//别忘了最后要更新Session中的标志
  29.         }
  30.         else
  31.         {
  32.             lblMessage.ForeColor = System.Drawing.Color.Red;
  33.             lblMessage.Text = "刷新提交表单";
  34.         }
  35.     }

  36.     //获得当前Session里保存的标志
  37.     public string GetToken()
  38.     {
  39.         if (null != Session["Token"])
  40.         {
  41.             return Session["Token"].ToString();
  42.         }
  43.         else
  44.         {
  45.             return string.Empty;
  46.         }
  47.     }

  48.     //生成标志,并保存到Session
  49.     private void SetToken()
  50.     {
  51.         Session.Add("Token", UserMd5(Session.SessionID + DateTime.Now.Ticks.ToString()));
  52.     }

  53.     //这个函数纯粹是为了让标志稍微短点儿,一堆乱码还特有神秘感,另外,这个UserMd5函数是网上找来的现成儿的
  54.     protected string UserMd5(string str1)
  55.     {
  56.         string cl1 = str1;
  57.         string pwd = "";
  58.         MD5 md5 = MD5.Create();
  59.         // 加密后是一个字节类型的数组
  60.         byte[] s = md5.ComputeHash(Encoding.Unicode.GetBytes(cl1));
  61.         // 通过使用循环,将字节类型的数组转换为字符串,此字符串是常规字符格式化所得
  62.         for (int i = 0; i < s.Length; i++)
  63.         {
  64.             // 将得到的字符串使用十六进制类型格式。格式后的字符是小写的字母,如果使用大写(X)则格式后的字符是大写字符
  65.             pwd = pwd + s[i].ToString("X");
  66.         }
  67.         return pwd;
  68.     } 
  69. }
    需要注意的地方:
    1 在页面第一次载入的时候要生成标志,以后就不用了。
    2 在表单处理的函数的最后,记得要更新标志。
    3 标志我选用了当前SessionID加上当前时间毫秒值,这样基本可以避免标志重复,之后进行了一次MD5,纯粹为了让标志短点儿,当然有一点点安全的意思,哈哈。

    所有代码就是这些,很简单,不知道是因为太简单还是大家有更好的方法,我在网上没有找到类似的代码,所以写下来和大家分享,如果有更好的方法,希望可以告诉我,因为好久不做Web开发了,怕是有很多新技术都不会了。