2015-09-13 3 views
0

J'ai rencontré le problème que les personnes sur mon site Web peuvent double-soumettre un formulaire. Ce n'est pas un problème du côté de la base de données, car les enregistrements sont enregistrés par la validation côté serveur. Cependant, il est important que j'invalide toute demande supplémentaire afin que les utilisateurs ne soient pas facturés deux fois (car les frais sont facturés avant que le compte ne soit créé). Je préférerais abandonner entièrement la requête plutôt que de la rediriger car cela ruinerait vraiment l'UX, alors que la suppression de la requête rendra le processus plus long, ce qui est un meilleur compromis.Comment éviter les doubles formulaires de soumission dans ExpressJS

Pour éclaircir les choses, j'utilise aussi le modèle post-redirect-get, mais cela ne résout pas ce problème, qui est en fait le problème le plus critique car il est le plus courant. La mise en œuvre d'une solution côté client sera faite pour la propreté de l'interface utilisateur, mais je ne peux pas faire confiance à l'utilisateur pour ne pas désactiver javascript ou en quelque sorte altérer.

J'ai une solution de middleware en utilisant la bibliothèque csurf qui ne fonctionne pas tout à fait.

var protect_route = function(req, res, next){ 
    if(!req.session.unique_requests){ 
    req.session.unique_requests = []; 
    } 

    var found = _.find(req.session.unique_requests, function(token){ 
    return token === req.body._csrf; 
    }); 

    if(!found){ 
    req.session.unique_requests.push(req.body._csrf); 
    next(); 
    } else { 
    next('route'); //drop the post, and skip anything else. 
    } 
}; 

var unlock_route = function(req, res, next){ 
    req.session.unique_requests = _.without(req.session.unique_requests, req.body._csrf); 
}; 

Je joins à mes itinéraires comme ceci:

router.post('/create', protect_route, function(req, res){ 
    // If it makes it in here, the route is protected and the 
    // request wasn't dropped. 
}); 

router.get('/review', unlock_route, function(req, res){ 
    // The form will once again be able to be submitted. 
}); 

Malheureusement, même si le middleware est appelé, si je Mash le bouton créer il déclencher une tonne de demandes, chacun tentera pour pousser le jeton sur le unique_requests, et le résultat est qu'il ne bloquera aucune demande pour quelques poussées jusqu'à ce que tout rattrape. Qu'est-ce que je fais mal ici, et comment puis-je le réparer?

EDIT: limitante le POST/créer semble être une possibilité, mais il ne semble pas une solution réelle je suppose. Cela peut fonctionner.

Répondre

1

Vous devriez être en mesure de le simplifier en manipulant simplement un drapeau avec les middleware, comme ceci:

var protect_route = function(req, res, next){ 
    if(!req.session.processing){ 
    req.session.processing = true; // set flag 
    next(); // continue in route chain 
    } else { 
    res.status(400).send() // end request here with 400 status code, drop everything 
    } 
} 

var unlock_route = function(req, res, next){ 
    req.session.processing = false; // unset flag 
    next(); 
}; 
+0

Malheureusement, cela souffre du même problème. L'écrasement du bouton crée plusieurs demandes. Le nœud ne les gère pas de manière synchrone, de sorte que le middleware ne le «réalise» qu'après la fin de la requête. Vous pouvez le constater par vous-même en plaçant des instructions console.log dans chacune des fonctions du middleware, puis en écrasant le bouton submit jusqu'à ce qu'il bloque. – Steve

+0

Juste avant d'appeler, forcez la session à sauvegarder. Habituellement, cela est fait à la fin de la demande. 'req.session.save (function() {next()})' - toujours pas parfait, mais si vous avez des choses asynchrones de longue durée qui se produisent après cela, devrait aider à minimiser la condition de la course. – furydevoid