Arrêter l'insertion de données dans une base de données deux fois
Im tout à fait nouveau pour PHP, je me demandais quelles méthodes/empêchements les autres programmeurs utilisent pour empêcher les données d'être entrées deux fois dans une base de données MySQL quand un utilisateur se rafraîchit sur la même page qu'un formulaire? Évidemment, ça arrive et j'ai besoin d'un bon moyen pour arrêter ça.
Merci, Ben
16 réponses
j'appelle cela une règle d'or de la programmation web:
ne répond jamais avec un corps à une POST-demande. Toujours faire le travail, puis répondre avec un emplacement: en-tête pour rediriger vers la page mise à jour afin que le navigateur le demande avec GET.
ainsi, le rafraîchissement ne vous fera pas de mal.
aussi, concernant une discussion ici dans les commentaires. Pour protéger de double affichage de, disons, doubleclicking accidentel le soumettre bouton, stockez un md5 () de votre formulaire dans un fichier texte, et comparez le nouveau formulaire md5 à celui stocké. S'ils sont égaux, vous avez un double poste.
traiter le formulaire, puis rediriger vers la page des résultats. Recharger alors seulement redisplays la page des résultats.
la réponse D'Ilya est correcte, je voulais juste ajouter un peu plus qu'il ne conviendrait dans un commentaire:
si la nouvelle soumission est dangereuse (revenir en arrière et soumettre à nouveau, recharger la page de résultat [si vous n'avez pas suivi les conseils D'Ilya], etc. J'utilise un "nonce" pour m'assurer que le formulaire ne peut passer qu'une fois.
sur la page de formulaire:
<?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>
dans la page de traitement:
<?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
après le formulaire a été soumis une fois, il ne peut pas être soumis à nouveau, à moins que l'utilisateur le remplisse intentionnellement une deuxième fois.
De l'évidence (je ne l'ai pas vu ici encore...): N'utilisez jamais d'OBTENIR pour publier des données, utilisez toujours des POST, de cette façon l'utilisateur au moins reçoit un avertissement si il ou elle essaie de rafraîchir ou de re-post de la page (au moins dans Firefox, mais je suppose que dans d'autres navigateurs).
soit dit en passant, si vous ne pouvez pas vous permettre d'avoir les mêmes données deux fois, vous devriez également envisager une solution MySQL avec une clé unique (peut être une combinaison de champs) et:
INSERT INTO ... ON DUPLICATE KEY UPDATE ...
je suis d'accord avec Ilya et j'ajoute que vous devriez utiliser du javascript client pour désactiver le bouton 'Soumettre' une fois qu'il a été cliqué ou présenter un dialogue modal (css peut vous aider ici) pour éviter les clics multiples sur le bouton Soumettre.
enfin, si vous ne voulez pas les données dans votre base de données deux fois, vérifiez aussi votre base de données pour les données avant d'essayer de les insérer. Si vous autorisez les enregistrements en double mais ne voulez pas d'insertions rapides répétées à partir d'une seule source, alors J'utiliserais un tampon d'heure / date et des champs d'adresse IP pour permettre un "lock-out" basé sur le temps dans mon code de soumission, c.-à-d. SI L'IP est la même et que la dernière heure de soumission était moins de 5 minutes auparavant alors ne pas insérer le nouvel enregistrement.
Espère que vous donne quelques idées.
vous pourriez vouloir consulter le post/Redirect / GET pattern la plupart des applications Web modernes mettre en œuvre, Voir http://en.wikipedia.org/wiki/Post/Redirect/Get
en plus des bonnes suggestions déjà mentionnées sur le fait d'éloigner l'utilisateur de la page de publication afin qu'un bouton rafraîchir et retour soient inoffensifs, une autre couche dans l'amélioration de votre stockage de données est d'utiliser UUID s comme clés dans votre table et laisser vos applications les générer.
ceux-ci sont également connus comme des GUIDs dans le monde de Microsoft et en PHP, vous pouvez en générer un via uniqid() en PHP. C'est une valeur hex de 32 caractères que vous devriez stocker dans un format de colonne hex/binaire, mais si la table n'est pas utilisée de manière intensive, CHAR(32) fonctionnera.
générez cet ID lorsque vous affichez votre formulaire comme une entrée cachée, et assurez-vous de marquer la colonne de la base de données est marquée comme la clé principale. Maintenant, si l'utilisateur réussit à retourner jusqu'à une page de publication, L'insertion échouera parce que vous ne pouvez pas avoir de clés dupliquées.
un bonus supplémentaire à cela est, si vous générez L'UUID en code, puis après avoir effectué un insert, vous n'aurez jamais besoin d'utiliser des requêtes inutiles récupérant la clé qui a été générée parce que vous la connaissez déjà. Il s'agit d'une belle prestation lorsque vous avez besoin D'insérer des articles pour enfants dans d'autres tables.
une bonne programmation est basée sur la stratification de votre travail, et ne dépend pas d'une chose pour fonctionner. Malgré le fait qu'il est courant pour les codeurs de compter sur les identifiants incrémentiels, ils sont l'un des moyens les plus paresseux pour construire une table.
Je m'appuie habituellement sur la contrainte d'index unique sql. http://dev.mysql.com/doc/refman/5.0/en/constraint-primary-key.html
vous devez passer une variable uniqid dans votre html à l'intérieur de la méthode showAddProductForm () et le même uniqid dans votre $_SESSION forexample:
public function showAddProductForm()
{
$uniId = uniqid();
$_SESSION['token'][$uniId] = '1';
$fields['token'] = 'token['.$uniId.']';
$this->fileName = 'product.add.form.php';
$this->template($fields);
die();
}
ensuite vous devez mettre une entrée cachée dans le code HTML à l'intérieur de votre formulaire avec la valeur de l'uniqid qui a été passé dans le HTML de la méthode showAddProductForm.
<input type="hidden" name="<?=$list['token']?>" value="1">
juste après l'événement submit, vous l'analyserez au début de la méthode addProduct (). Si le token existe dans $_SESSION et il a une valeur égale à l'intérieur de ce tableau, alors c'est les nouvelles requêtes. Redirigez - le vers la page appropriée et désactivez le token et continuez l'insertion. Sinon c'est à partir de la page de rechargement ou d'une demande répétitive, redirigez-le vers addprodeuctpage
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, '');
}
}
peut-être vous demandez-vous pourquoi un tableau de tokens pourquoi pas une seule variable. Parce que dans le Panneau d'administration les utilisateurs ouvrent les onglets multi et insèrent dans les onglets multi de sorte que cet algorithme échouera s'ils utilisent multi onglet.
merci à l'OMS de comprendre cette méthode malekloo
Eh bien, tout d'abord, pour minimiser, vous devriez faire en sorte qu'ils doivent faire le formulaire post pour insérer les données. De cette façon, au moins ils obtiendront cette jolie petite boîte de dialogue de confirmation demandant s'ils veulent vraiment la soumettre de nouveau.
pour obtenir plus compliqué que cela, vous pourriez mettre une clé cachée d'utilisation d'une fois dans chaque formulaire, et une fois que le formulaire avec cette clé a été soumis, afficher une erreur quand ils essaient de soumettre un formulaire avec la même clé. Pour créer cette clé, vous vous voulez probablement utiliser quelque chose comme un guide.
ajoute un champ caché avec une chaîne aléatoire (produit par md5(uniqid())
par exemple), crée un champ dans la base de données pour cette chaîne et le rend UNIQUE.
vous pouvez utiliser un token pour empêcher la page d'être traitée à nouveau! Une telle procédure est utilisée dans beaucoup de cadres web !
le modèle que vous devez utiliser est le"modèle de Token Synchronizer"! Si vous avez une application orientée service, vous pouvez enregistrer votre statut dans la base de données.
les données peuvent être envoyées via JavaScript ou par un champ de formulaire caché.
vous devriez également avoir un regard sur libaries avec out of the box support for des choses comme cela! Graal est un!
Voir: http://www.grails.org/1.1-Beta3+Libération+Notes ...
<g:form useToken="true">
...
withForm {
// good request
}.invalidToken {
// bad request
}
..
mes deux cents:
- rediriger l'utilisateur vers la même page après l'envoi des données et de vérifier
if(isset($_POST['submit'])
autres informations utiles pour les cas similaires:
-
donnez un coup d'oeil à LOCK TABLES in MySQL
-
boutons désactivés via JavaScript après le premier clic
POE (Post Once Exactly) est un pattern HTTP visant à avertir le client de bloquer double submits en utilisant un en-tête propriétaire ...
GET /posts/new HTTP/1.1
POE: 1
...
... mais est toujours dans les spécifications.
http://www.mnot.net/drafts/draft-nottingham-http-poe-00.txt
je pense que la nonce ci-dessus est une bonne solution. Bien que le stockage de la nonce comme un la variable session introduira quelques erreurs si le client tente d'effectuer des messages simultanés à partir de plusieurs onglets. Peut-être mieux ...
$_SESSION['nonces'][] = $nonce;
... et. ..
if (in_array($_POST['nonce'], $_SESSION['nonces'])) {
... pour tenir compte de nonces multiples (nonci? noncei?).
essayez d'inclure quelque chose dans vos formulaires pour éviter la double soumission, de préférence en même temps protéger contre la falsification de demandes intersite. Je recommande l'utilisation de ce que j'appelle un formkey , qui est un champ à usage unique qui identifie de façon unique une soumission de formulaire, le liant à un utilisateur individuel à une adresse individuelle. Le concept va sous d'autres noms aussi, mais la courte note que j'ai liée à l'explique assez bien.
la meilleure façon d'éviter l'insertion d'un enregistrement en double sur la page de rafraîchissement est, après avoir inséré des enregistrements dans la base de données sur le clic de bouton, il suffit d'ajouter cette ligne:
Response.Write("<script>location.href='yourpage.aspx'</script>");