为什么两次按下按钮时都没有两次提交此表格?

时间:2018-07-15 10:39:59

标签: php firefox post

编辑25.07.2018:正如Pawnesh Kumar在回答中所说,这似乎是浏览器问题。如果我在Firefox中多次按下该按钮,则以下脚本将仅发送一个POST请求,但是如果我在Chrome中执行相同的操作,则每次点击都会收到一个POST请求。

不过,我可以在01:00在video中复制该问题。这意味着当我使用Authentication安装Laravel时,如果我两次单击登录表单中的Submit按钮,Firefox将发送2个请求。

为什么当多次单击按钮时,Firefox为什么有时发送多个POST请求,有时仅发送一个请求?


我有一个用户

id | name
 1 | John

其中字段id是一个主整数,自动增量密钥。当我提交一个只有一个按钮的虚拟表单时,这将插入一个名称为John的新记录。现在,这就是我观察到的:

  • 如果我一次提交了表单,请在浏览器中返回,再次提交,然后 我在数据库中找到两个新行。

  • 如果我通过在Add上单击两次(或二十次)来提交表单 按钮,那么数据库中只有一个新行。

那是为什么?我希望如果我多次单击“提交”按钮,那么表单将发送多个请求-并插入多行。

那是我的表格:

<form action="/test.php" method="POST">
  <input type="submit" value="Add">
</form>
提交给test.php

<?php
$servername = "localhost";
$username = "adam";
$password = "password";
$dbname = "test-db";

$conn = new mysqli($servername, $username, $password, $dbname);       
$sql = "INSERT INTO user (name) VALUES ('John')";
if ($conn->query($sql) === TRUE) {
    echo "New record created successfully";
}     
$conn->close();

sleep(4);

由于sleep部分,我可以连续单击Add按钮。但是,无论我在加载时多久单击一次Add按钮,数据库中都只有一个新行。

在我的access.log文件中,单击按钮二十次后,我也只找到一个GETPOST请求:

  

2001:****:****:4400:****:****:****:****--[25 / Jul / 2018:11:30:03 +0200]“ GET /test/form.php HTTP / 1.1” 200301“-”“ Mozilla / 5.0(X11; Ubuntu; Linux x86_64; rv:57.0)Gecko / 20100101 Firefox / 57.0”“ ****** * .net”

     

2001:****:****:4400:****:****:***:****--[25 / Jul / 2018:11:30:34 + [0200]“ POST /test/test.php HTTP / 1.1” 200 31“ http://********.net/test/form.php”“ Mozilla / 5.0(X11; Ubuntu; Linux x86_64; rv:57.0)Gecko / 20100101 Firefox / 57.0“” ********。net“


备注:

我已经阅读了有关防止多次提交的技术 在backendfrontend中。因此,我认为应该可以通过多次单击按钮来多次提交表单。

我还在Wiki文章Post/Redirect/Get中读到,如果用户多次过快提交表格,这种模式将无法阻止:

  

如果网络用户由于服务器延迟而在完成首次提交之前刷新,从而导致某些用户代理中存在重复的POST请求。

还在video的1:00,有人双击按钮并收到错误消息,因为他提交了两次。

4 个答案:

答案 0 :(得分:2)

注意:我不是专家,所以我可能是错的。这只是我的意见。

在我看来,这似乎是一个浏览器怪癖。似乎Firefox故意忽略了所有额外的请求。

Chrome

对于Chrome,如果您多次单击添加按钮,则会向服务器发送多个请求。最初,所有多余的请求都会被取消(您可以了解有关canceled here的更多信息),因为服务器没有响应。但是,一旦服务器做出响应(即,一旦睡眠时间到了),所有请求都将立即得到处理。我不知道为什么,但是服务器仅在第一个请求时进入睡眠状态。其余请求将立即处理。

Chrome

边缘

如果多次单击该按钮,Edge还会发送多个请求。

Edge

Firefox

Firefox有点奇怪。无论您单击该按钮多少次,它都只会发送一个请求。所有多余的请求都将被忽略。

Firefox

答案 1 :(得分:1)

  1. 您怎么知道您的表单没有被提交两次或多次?

事实上,您在数据库中没有多个行并不能证明这一点。也许列“名称”在您的表中是唯一的?

  1. 检查Web服务器的日志。 apache的示例:sudo cat /var/log/apache2/access.log

您将在其中找到收到的请求数量的信息。

答案 2 :(得分:1)

这是浏览器。

我相信您是在谈论用户多次单击按钮时的情况。在后台时,页面开始重新加载。

当我们点击提交按钮时,浏览器将请求发送到服务器。直到响应接收到一些浏览器后,才会显示旧页面(过期的页面),同时简单地清除视图。

在Firefox中,即使单击多次,它也不会触发任何事件。每次用户单击时,Chrome都会在其中将请求提交到服务器。

因此,最重要的是浏览器处理事物的方式。您的日志还显示您正在使用Firefox,情况就是如此。

答案 3 :(得分:0)

可能是因为您用dict专门锁定了一个文件LOCK_EX,然后尝试用flock对其进行写入,这在幕后用file_put_contents打开了一个新指针。 ,并且由于您已经完全锁定了文件,因此无法将其写入文件。

尝试将fopen替换为file_put_contents,并像这样fwrite($fp, $current, strlen($current))一样向您的fopen添加一个附加标志

这是否重现了多次写入的问题?

更新

由于Laravel的CSRF保护,导致您提供的视频中的令牌异常不匹配。

Laravel中的每个表单都需要有一个fopen($file, a+),或者您需要在标头中提供{{ csrf_field() }}来处理AJAX请求。

表单的隐藏字段中的crsf_token与Laravel用户会话中存储的值相同。当您双击表单上的快速提交时,第一个请求与第一个csrf_token一起发送,当它命中Laravel时,将在表单请求中发送的令牌与会话中存储的令牌进行比较(如果相等),请求通过,并在会话中生成新的csrf_token

由于您没有重新加载视图,因此无法在隐藏的表单字段中呈现新令牌,因此,当第二个请求命中时,会话中将有一个新的csrf_token而在会话中有一个旧的csrf_token第二种形式的请求,因此引发了异常。

更新2

就像我说的是flock一样,请尝试使用此代码,多次提交问题就可以解决

<?php

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
       $file = 'file.txt';
       $fp = fopen($file, 'a+');

       if (flock($fp, LOCK_EX)) {
           fwrite($fp, "+", 1);
           flock($fp, LOCK_UN);
       }
       sleep(2);
    }
 ?>
<!DOCTYPE html>
<html lang="en">
    <head>

    </head>
    <body>
        <form action="/" method="POST">
            <button type="submit">Submit</button>
        </form>
    </body>
</html>