编辑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文件中,单击按钮二十次后,我也只找到一个GET
和POST
请求:
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“
备注:
我已经阅读了有关防止多次提交的技术 在backend或frontend中。因此,我认为应该可以通过多次单击按钮来多次提交表单。
我还在Wiki文章Post/Redirect/Get中读到,如果用户多次过快提交表格,这种模式将无法阻止:
如果网络用户由于服务器延迟而在完成首次提交之前刷新,从而导致某些用户代理中存在重复的POST请求。
还在video的1:00,有人双击按钮并收到错误消息,因为他提交了两次。
答案 0 :(得分:2)
注意:我不是专家,所以我可能是错的。这只是我的意见。
在我看来,这似乎是一个浏览器怪癖。似乎Firefox故意忽略了所有额外的请求。
Chrome
对于Chrome,如果您多次单击添加按钮,则会向服务器发送多个请求。最初,所有多余的请求都会被取消(您可以了解有关canceled
here的更多信息),因为服务器没有响应。但是,一旦服务器做出响应(即,一旦睡眠时间到了),所有请求都将立即得到处理。我不知道为什么,但是服务器仅在第一个请求时进入睡眠状态。其余请求将立即处理。
边缘
如果多次单击该按钮,Edge还会发送多个请求。
Firefox
Firefox有点奇怪。无论您单击该按钮多少次,它都只会发送一个请求。所有多余的请求都将被忽略。
答案 1 :(得分:1)
事实上,您在数据库中没有多个行并不能证明这一点。也许列“名称”在您的表中是唯一的?
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>