php 停止将数据插入数据库两次
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/638494/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Stop data inserting into a database twice
提问by Ben McRae
Im quite new to PHP, i was wondering what methods/preventions other programmers use to stop data being entered twice into a MySQL database when a user refreshes on the same page as a form? Obviouly it happens and i need a good way to stop this.
我对 PHP 很陌生,我想知道当用户在与表单相同的页面上刷新时,其他程序员使用什么方法/预防措施来阻止数据两次输入 MySQL 数据库?显然它发生了,我需要一个好方法来阻止它。
Thanks, Ben
谢谢,本
回答by Ilya Birman
I call this a golden rule of web programming:
我称之为网络编程的黄金法则:
Never ever respond with a body to a POST-request. Always do the work, and then respond with a Location: header to redirect to the updated page so that browser requests it with GET.
永远不要用正文响应 POST 请求。总是做这项工作,然后用 Location: 标头响应以重定向到更新的页面,以便浏览器使用 GET 请求它。
This way, refreshing will not do you any harm.
这样,提神不会对您造成任何伤害。
Also, regarding a discussion here in comments. To protect from double posting from, say, accidental doubleclicking the Submit button, store an md5() of your form in a text file, and compare the new form's md5 to the stored one. If they are equal, you are having a double post.
此外,关于评论中的讨论。为了防止重复发布,例如,意外双击提交按钮,将表单的 md5() 存储在文本文件中,并将新表单的 md5 与存储的进行比较。如果它们相等,则您有双重职位。
回答by chaos
Process the form, then redirect to the results page. Reload then only redisplays the results page.
处理表单,然后重定向到结果页面。重新加载然后只重新显示结果页面。
回答by James Socol
Ilya's answer is correct, I just wanted to add a little more than would fit in a comment:
Ilya 的回答是正确的,我只是想添加一些超出评论的内容:
If resubmission is dangerous (going back and submitting again, reloading the result page [if you haven't taken Ilya's advice], etc.) I use a "nonce" to make sure the form can only go through once.
如果重新提交是危险的(返回并再次提交,重新加载结果页面 [如果您没有接受 Ilya 的建议] 等)我使用“nonce”来确保表单只能通过一次。
On the form page:
在表单页面上:
<?php
@session_start(); // make sure there is a session
// store some random string/number
$_SESSION['nonce'] = $nonce = md5('salt'.microtime());
?>
// ... snip ...
<form ... >
<input type="hidden" name="nonce" value="<?php echo $nonce; ?>" />
</form>
In the processing page:
在处理页面:
<?php
if (!empty($_POST)) {
@session_start();
// check the nonce
if ($_SESSION['nonce'] != $_POST['nonce']) {
// some error condition
} else {
// clear the session nonce
$_SESSION['nonce'] = null;
}
// continue processing
After the form has been submitted once, it cannot be submitted again, unless the user intentionally fills it out a second time.
表单提交一次后,不能再次提交,除非用户有意第二次填写。
回答by jeroen
To state the obvious (I have not seen it here yet...): Never use GET to post data, always use POST, that way the user at least gets a warning if he or she tries to refresh /re-post the page (at least in Firefox, but I suppose in other browsers as well).
声明一个明显的(我还没有在这里看到它......):永远不要使用 GET 发布数据,始终使用 POST,这样用户在尝试刷新/重新发布页面时至少会收到警告(至少在 Firefox 中,但我想在其他浏览器中也是如此)。
By the way, if you cannot afford to have the same data twice, you should also consider a MySQL solution with a unique key (can be a combination of fields) and:
顺便说一句,如果您不能承受两次相同的数据,您还应该考虑使用唯一键(可以是字段组合)的 MySQL 解决方案,并且:
INSERT INTO ... ON DUPLICATE KEY UPDATE ...
回答by jmoz
You might want to check out the POST/Redirect/GET pattern most modern web apps implement, see http://en.wikipedia.org/wiki/Post/Redirect/Get
您可能想查看大多数现代 Web 应用程序实现的 POST/Redirect/GET 模式,请参阅http://en.wikipedia.org/wiki/Post/Redirect/Get
回答by Lazarus
I'd agree with Ilya there and add that you should use some client javascript to disable the 'submit' button once it's been clicked or present a modal dialog (css can help you here) to avoid multiple clicks on the submit button.
我同意 Ilya 的意见,并补充说您应该使用一些客户端 javascript 来禁用“提交”按钮,一旦它被点击或呈现一个模态对话框(css 可以在这里帮助您)以避免多次点击提交按钮。
Lastly, if you don't want the data in your database twice then also check your database for the data before you try to insert it. If you do allow duplicate records but don't want rapid repeat insertions from a single source then I'd use a time/date stamp and IP address fields to allow for a time-based 'lockout' in my submission code, i.e. if the IP is the same and the last submission time was less than 5 minutes ago then don't insert the new record.
最后,如果您不希望数据库中的数据出现两次,那么在尝试插入数据之前还要检查数据库中的数据。如果您确实允许重复记录但不想从单一来源快速重复插入,那么我将使用时间/日期戳和 IP 地址字段来允许在我的提交代码中基于时间的“锁定”,即如果IP 相同且上次提交时间小于 5 分钟前,则不要插入新记录。
Hope that gives you some ideas.
希望能给你一些想法。
回答by TravisO
In addition to the good suggestions already mentioned about taking the user away from the posting page so a refresh and back button are harmless, another layer in improving your data storage is to use UUIDs as keys in your table and let your applications generate them.
除了已经提到的关于将用户从发布页面移开以便刷新和后退按钮无害的好建议之外,改进数据存储的另一层是使用UUID作为表中的键并让您的应用程序生成它们。
These are also known as GUIDs in the Microsoft world and in PHP you can generate one via uniqid() in PHP. This is a 32 character hex value which you should store in a hex/binary column format but if the table isn't going to be heavily used than CHAR(32) will work.
这些在 Microsoft 世界中也称为 GUID,在 PHP 中,您可以通过 PHP 中的 uniqid() 生成 GUID。这是一个 32 个字符的十六进制值,您应该以十六进制/二进制列格式存储它,但如果表不会被大量使用,则 CHAR(32) 将起作用。
Generate this ID when you display your form as a hidden input, and make sure to mark the database column is marked as the primary key. Now if the user does manage to go all the way back to a posting page, the INSERT will fail because you can't have duplicate keys.
当您将表单显示为隐藏输入时生成此 ID,并确保将数据库列标记为主键。现在,如果用户确实设法一直返回到发布页面,则 INSERT 将失败,因为您不能拥有重复的键。
An extra bonus to this is, if you generate the UUID in code, then after you perform an insert you'll never need to use wasteful queries retrieving the key that was generated because you'll already know it. This is a nice benefit when you need to INSERT child items into other tables.
一个额外的好处是,如果您在代码中生成 UUID,那么在您执行插入之后,您将永远不需要使用浪费的查询来检索生成的密钥,因为您已经知道它。当您需要将子项插入其他表时,这是一个很好的好处。
Good programming is based upon layering your work, not relying on 1 thing to work. Despite how common it is for coders to rely on incremental IDs, they are one of the laziest ways to build a table.
好的编程是基于分层你的工作,而不是依赖于一件事来工作。尽管编码人员依赖增量 ID 是多么普遍,但它们是构建表格的最懒惰的方法之一。
回答by Carlo
I usually rely on the sql UNIQUE index Constraint. http://dev.mysql.com/doc/refman/5.0/en/constraint-primary-key.html
我通常依赖于 sql UNIQUE 索引约束。 http://dev.mysql.com/doc/refman/5.0/en/constraint-primary-key.html
回答by VeRJiL
You have to pass an uniqid variable into your html inside the showAddProductForm() method and the same uniqid into your $_SESSION forexample:
您必须在 showAddProductForm() 方法中将一个 uniqid 变量传递到您的 html 中,并将相同的 uniqid 传递到您的 $_SESSION 中,例如:
public function showAddProductForm()
{
$uniId = uniqid();
$_SESSION['token'][$uniId] = '1';
$fields['token'] = 'token['.$uniId.']';
$this->fileName = 'product.add.form.php';
$this->template($fields);
die();
}
Then you have to put a hidden input in HTML code inside your form with the value of the uniqid that has been passed into the HTML from showAddProductForm method.
然后,您必须在表单内的 HTML 代码中放置一个隐藏输入,其中 uniqid 的值已从 showAddProductForm 方法传递到 HTML 中。
<input type="hidden" name="<?=$list['token']?>" value="1">
right after the submit event you will analyze it in the beginning of the addProduct() method. If the token exists in the $_SESSION and it has an equal value inside that array, then this is the new reques. Redirect him to the proper page and unset the token and continue insert. Else it is from the reloading page or a repetitive request, redirect him to addProdeuct page
在提交事件之后,您将在 addProduct() 方法的开头对其进行分析。如果令牌存在于 $_SESSION 中并且它在该数组中具有相等的值,那么这就是新的请求。将他重定向到正确的页面并取消设置令牌并继续插入。否则来自重新加载页面或重复请求,将他重定向到 addProdeuct 页面
public function addProducts($fields)
{
$token_list = array_keys($fields['token']);
$token = $token_list['0'];
if (isset($_SESSION['token'][$token]) and $_SESSION['token'][$token] == '1') {
unset($_SESSION['token'][$token]);
} else {
$this->addAnnounceForm($fields, '');
}
}
Maybe you ask why an array of tokens why not a single variable. Because in admin Panel users open multi tabs and inserting in multi tabs so this algorithm will fail if they use multi tabs.
也许你会问为什么一个令牌数组为什么不是单个变量。因为在管理面板中,用户打开多标签并插入多标签,所以如果他们使用多标签,这个算法将失败。
Special thanks to who figure out this method malekloo
特别感谢谁找出了这个方法malekloo
回答by KevBurnsJr
POE(Post Once Exactly)is an HTTP pattern aimed at warning the client to block double submits using a proprietary header ...
POE (Post Once Exactly)是一种 HTTP 模式,旨在警告客户端使用专有标头阻止双重提交......
GET /posts/new HTTP/1.1
POE: 1
...
... but is still in specification.
...但仍在规范中。
http://www.mnot.net/drafts/draft-nottingham-http-poe-00.txt
http://www.mnot.net/drafts/draft-nottingham-http-poe-00.txt
I think the above nonce is a good solution. Though storing the nonce as a discrete session variable will introduce some errors if the client is attempting to perform simultaneous posts from multiple tabs. Maybe better to ...
我认为上述 nonce 是一个很好的解决方案。尽管如果客户端尝试从多个选项卡同时执行帖子,将 nonce 存储为离散会话变量会引入一些错误。也许更好...
$_SESSION['nonces'][] = $nonce;
... and ...
... 和 ...
if (in_array($_POST['nonce'], $_SESSION['nonces'])) {
... to allow for multiple nonces (nonci? noncei?).
...允许多个随机数(nonci?noncei?)。

