Reputation: 29119
Edit 25.07.2018: As Pawnesh Kumar said in the answer, this seems to be a browser issue. If I hit the button multiple times in Firefox the below script will only send one POST
request, but if I do the same in Chrome I get a POST
request for each click.
However, I can replicate the problem in the video at 01:00. This means when I install Laravel with Authentication, then if I click the submit button in the login form twice, Firefox will send 2 requests.
Why is Firefox sometimes sending multiple POST
request and sometimes only one, when clicking multiple times on the button?
I have a user table
id | name
1 | John
where the field id
is a primary, integer, auto-incremet key. When I submit a dummy form that only has one button, then this will insert a new record with name John
. Now this is what I observed:
If I submit the form once, go back in the browser submit it again, then I find two new rows in the DB.
If I submit the form by clicking twice (or twenty times) on the Add
button, then there is only a single new row in the DB.
Why is that? I would expect that if I hit the submit button multiple times, then the form will send multiple requests - and inserts multiple rows.
Thats my form:
<form action="/test.php" method="POST">
<input type="submit" value="Add">
</form>
which submits to 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);
Because of the sleep
part, I can click on the Add
button multiple times in a row. However, no matter how often I click the Add
button while loading, there is only one new row in the DB.
In my access.log file I also find only one GET
and POST
request after clicking the button twenty times:
2001:****:****:4400:****:****:****:**** - - [25/Jul/2018:11:30:03 +0200] "GET /test/form.php HTTP/1.1" 200 301 "-" "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"
Remark:
I have read about techniques to prevent multiple submissions either in the backend or frontend. Thus I think it should be possible to submit the form multiple times by hitting the button multiple times.
I also read in the wiki article Post/Redirect/Get that this pattern can not prevent if a user sumbits a form multiple times too quick:
If a web user refreshes before the initial submission has completed because of server lag, resulting in a duplicate POST request in certain user agents.
Also in this video at 1:00 someone double clicks on a button and gets an error because he submitted twice.
Upvotes: 1
Views: 4182
Reputation: 861
Note: I'm no expert, so I might be wrong. This is just my opinion.
To me, this seems like a browser quirk. It seems like Firefox intentionally ignores all the extra requests.
Chrome
In case of Chrome, if you click on the add button multiple times, multiple requests are sent to the server. Initially, all the extra requests are canceled (you can learn more about canceled
here) since the server is not responding. However, once the server responds (i-e, once the sleep time is up), all the requests are processed immediately. I don't know why, but the server only sleeps for the first request. The rest of the requests are processed immediately.
Edge
Edge also sends multiple requests if you click the button multiple times.
Firefox
Firefox is a little weird. It just sends one request no matter how many times you click the button. All the extra requests are ignored.
Upvotes: 4
Reputation: 494
It is a browser thing.
I believe you are talking about the situation when a user clicks multiple times on the button. While in the background the page start reloading.
When we hit the submit button browser send the request to server. Until the response receive some browser show the old(expired) page while some simple clean the view.
In Firefox, it won't fire any event in spite of clicking many times. Wherein Chrome it submit the request to the server every time user click.
So, the bottom line it is the way the browser handles things. Your logs also showing you were using Firefox so that the case.
Upvotes: 1
Reputation: 1963
The fact, that you do not have multiply rows in database doesn't proof that. Maybe column "name" is unique in your table?
sudo cat /var/log/apache2/access.log
There you will find info of how many request you recieved.
Upvotes: 1
Reputation: 2200
It might be because you lock a file exclusively LOCK_EX
with flock
and then try to write to it with file_put_contents
which under the hood opens a new pointer with fopen
, and since you already locked the file exclusively it can't write to your file.
Try replacing the file_put_contents
with fwrite($fp, $current, strlen($current))
, and add an append flag to your fopen
like this fopen($file, a+)
Does this reproduce the issue of multiple writes?
UPDATE
The token exception mismatch from the video you provided happens because of the Laravel's CSRF protection.
Every form in Laravel needs to have a {{ csrf_field() }}
or you need to provide the crsf_token
in the Header for AJAX requests.
That csrf_token
in the form's hidden field has that same value stored in the Laravel's user session. When you double click submit fast on a form, the first request is sent with the first csrf_token
, when it hits Laravel the token sent in the form request and the token stored in the session are compared, if they are equal the request passes, and a new csrf_token
is generated in the session.
Since you did not reload the view the new token couldn't be rendered in the hidden form field, so by the time the second request hits we have a new csrf_token
in the session and the old one in the second form request and thus the exception is thrown.
UPDATE 2
As I said it was the flock
, try this code and the multiple submit issue will work
<?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>
Upvotes: 0