禁止表单多次提交

来源:百度文库 编辑:神马文学网 时间:2024/07/03 08:23:17



禁止表单多次提交,表单提交一次后提交键变灰色






 

 

1. 利用 javascript :

    1.1 document.form1.submit();后加
document.body.innerHtml = "

Waiting...
"; //当然这里的html代码就由你发挥了,还可把这段写成函数,这样维护就方便了!

这一处理,就让用户在等待提交时不会误以为没提交而重复按提交按钮!

    1.2 按钮设置

   ……


…………


2. 利用继承制作防止重复提交按钮

我们浏览很多论坛发表帖子时,单击“发表”按钮,这个按钮就会变成灰色,并且还有提示如“正在提交,请稍候...”等,这样做一方面让用户看到效果,避免长时间等待网页的烦躁,另一方面又防止了重复提交。

在.NET中没有类似的功能,不过我们已经知道他就是一个普通按钮的基础上多加了一个功能而以。在.NET中,我们可以巧妙利用类的继承来制作这种按钮。
这里我是用C#语言作为范例,其他语言可以举一反三得到应用,这里不再赘述。

我们需要自己写一个类,这个类继承自System.Web.UI.WebControl.Button:

public class ClickOnceButton : System.Web.UI.WebControl.Button


我们知道要实现这种功能需要借助JS脚本,.NET的控件提供了一个Attributes属性用来添加任何想要的客户端属性。我们需要在客户端的onclick中写入:this.disabled=true,来达到使按钮变灰,另外,再用一句:this.value="正在提交,请稍候...",来使的按钮的文字改变。
把插入脚本这一动作放在了控件加载的时候进行。因此,我们重写OnLoad方法:

         protected override void OnLoad(EventArgs e)
         {
             this.Attributes.Add("onclick","this.disabled=true;this.value=\"正在提交,请稍候...\"");
             base.OnLoad (e);
         }


上面这句向客户端属性中onclick添加了这些语句。记得重写函数是不要忘记最后要调用基类的OnLoad方法。

编译,然后就可以在网页上使用了。

你可以作为一个单独的控件库项目来写这个东西,然后从工具箱上添加上,把他们拖动到网页中,就可使用了。大家还可以扩充一些实用的功能。具体控件的编程美化等等细节此处不再赘述。

 

4. 防止重复提交 JAVA 解决

1、当用户进行的是Refresh/Reload/Back/Forward操作、以及先Back再Submit操作时,仅仅是reloading先前的结果页。

       2、当用户重复提交同一个任务操作时,后台服务接收并处理第一次提交的任务,后面提交不起作用(不转向也不提示)。

       3、该功能具有公用性。

基本形成思路:

       (1)、在basic filter中实现公用性

                if(true){//问题1:如何确定是否为重复提交

                         ...

                         chain.doFilter(request,response);

                }else{

                         //问题2:如何实现不转向、不提示也不显示空白页

                }

       (2)、网上资料概括

                a、提交表单后按钮变灰/隐藏提交按钮

                b、在js里设置全局变量,提交后修改该变量的值,依据变量的值判断是否重复提交

                         var flag=true;

                         function checkForm(){

                                   if (flag==false){

                                            return;

                                   }

                        

                                   flag=false;

                                   document.form1.submit();

                        

                         }

                c、struts (webwork没有找到这个资料)

                         //验证事务控制令牌,会自动根据session中标识生成一个隐含input代表令牌,防止两次提交

                         在action中:

                        

                                //

                        

                                if (!isTokenValid(request))

                                    errors.add(ActionErrors.GLOBAL_ERROR,

                                               new ActionError("error.transaction.token"));

                                resetToken(request); //删除session中的令牌

                        

                         action有这样的一个方法生成令牌华

                            protected String generateToken(HttpServletRequest request) {

                        

                                HttpSession session = request.getSession();

                                try {

                                    byte id[] = session.getId().getBytes();

                                    byte now[] =

                                        new Long(System.currentTimeMillis()).toString().getBytes();

                                    MessageDigest md = MessageDigest.getInstance("MD5");

                                    md.update(id);

                                    md.update(now);

                                    return (toHex(md.digest()));

                                } catch (IllegalStateException e) {

                                    return (null);

                                } catch (NoSuchAlgorithmException e) {

                                    return (null);

                                }

                        

                            }          

                d、用户使用浏览器时,可以经常使用向后的按钮,因此就有可能重复提交一个他们已经提交过的form,这样就会带来一个重复事务处理的问题。同样,一个用户也可能在接收到一个确认的页面之前按下停止的按钮,接着再次提交同一个form。对于这些情况,我们都想跟踪并且禁止这些重复的提交,我们可以使用一个控制servlet来提供一个控制点,以解决这个问题。

                  同步记号(Synchronizer (or Dvu) Token)

               

                  这个策略是为了解决重复的form提交问题。一个同步的记号被设置在一个用户的Session中,并且包含在返回到客户的每一个form中。当form被提交时,form中的同步标记就和Session中的同步标记作对比。在form首次提交的时候,这两个标记应该是一样的。如果标记不一样,那么该form就会禁止提交,一个错误就会返回给用户。在用户提交一个form时,如果按下浏览器中的后退按钮并尝试重新提交同一个form时,标记就会出现不匹配的现象。

               

                  另一方面,如果两个标记值匹配,那么我们就可以确信整个流程是正确的。在这种情况下,

5. 如何防止用户重复提交数据

简单的解决方案:
最简单的方式就是当用户提交之后,在你的服务器端控件的代码中使用Response.Redirect("selfPage")语句。但是大多的数包括我都不使用这种方法。

多次提交:
请注意:这篇文章并不是有关如何防止在一个页面中多次提交。这篇文章是教你在提交了请求之后如何防止页面进行刷新。情况是这样的,当用户提交了按钮之后,最终用户就不能再点击提交按钮了。但是这样最终用户仍然可以通过点击浏览器的刷新按钮来提交数据。如果要防止多次提交你可以去 http://metabuilders.com/网上找一些资料,它那里有一个提交控件可以用。

常规的解决方案
习惯的解决方法是存储Session的ID和当提交时ViewState中存储的SessionID相比较来防止用户刷新屏屏幕。前提你的程序中允许了自动回发,如果不是的话,就得在hidden field存储这个变量了。下面给出一个典型的例子。在Page_Load事件中你存储了第一次提交时的SessionID和一个时间戳。
protected System.Web.UI.WebControls.Button SubmitButton;

protected System.Web.UI.WebControls.Label RefreshID;     

private void Page_Load(object sender, System.EventArgs e)

{
     if (RefreshID.Text.Length == 0)
     {
            RefreshID.Text = Session.SessionID+DateTime.Now.Ticks.ToString();
      }
}

private void Button1_Click(object sender, System.EventArgs e)

{
      string sesToken = (string) Session[FrameworkConst.SYNC_CONTROL_KEYWORD];      string pageToken = RefreshID.Text;
      if (sesToken != null && sesToken != pageToken)
      {
            Response.Write("The Refresh was performed after submit.");
     }
      else
      {
            // do your processing here to avoid Refresh trap
            Response.Write("The processing is done here. Disabling submit
button so that user can not perform multiple submit.");
            Response.Write("But still user can peform Refresh on page.");
      }

      Session[FrameworkConst.SYNC_CONTROL_KEYWORD] =       Session.SessionID+DateTime.Now.Ticks.ToString();
      RefreshID.Text = sesToken;         
      SubmitButton.Enabled = false;

}

不同的解决方案:
幸运的事,asp.net提供了一些更简单的方法。上面的解决方案的缺点是我们要在控钮的事件中自己决定一些逻辑问题。设想一下,如果在你的解决方案中有成百个要提交的页面,你就得写上许多个这样的逻辑。自定义web控件和HTTP MOdules提供了相同的解决法。你可以将这个控件入在你需要控制的页面上,它就可以起作用了。当然,并不是所有的情况都需要的,比如说搜索页面是允许用户刷新的。但是,在页面中有插入、更新、删除数据库的操作时,控制刷新按钮是绝对有必要的。

下面来看一下上面的方案是如何工作的。
第一步:需要在System.web节中注册HTTP Module模块。

  

第二步:要在控制页的页面内放入我们开发好的控件。

工作原理:
它的工作原理和前面普通讲的是差不多的。只是这里提供了一个通用的方法。这里提供了一种通用的方式。在我看来,如果你有一些好的模式,将大大的加快你的开发速度。

我们需要在第一次提交时在Session中存储标记,并在请求时比较它们是否不同。通过HTTP handler,我们在Session中存储标记。有这样的一个事件PreRequestHandlerExecute 我们可以通过它找到Session,如果是其它事件的话Session是不存在的,比如BeginRequest 事件。在这个事件中比较两者的值,如果不同则证明是Refresh事件。这时你可以添加自己的处理方法,我一般是将转向一个页面告诉用户不能反复提交。
private void OnPreRequestHandlerExecute(object source, EventArgs e)

{

      HttpContext context = ((HttpApplication) source).Context;

      string _keyword = FrameworkConst.SYNC_CONTROL_KEYWORD;

      string sesToken = (string) context.Session[_keyword];

      string reqToken = context.Request.Params[_keyword];
//如果没有提交过,则保存Session和标记值

      if(reqToken != FrameworkConst.BYPASS_SYNC_KEYWORD)
      {
            context.Session[_keyword] =             context.Session.SessionID+DateTime.Now.Ticks.ToString();
      }
      if(reqToken != null && reqToken != sesToken)
      {
            string path=context.Request.ApplicationPath+
            &"/Common/SyncControl.aspx?returnUrl="+
            &context.Request.Url.AbsolutePath;
            context.Server.Transfer(path);
      }
}

SyncControl 控件将建立一个hidden input field 将在 HTTP module中设置的Session保存起来。 HttpModule和Http Handler (比较与区别)http://blog.csdn.net/chengfong/archive/2009/04/17/4088530.aspx