如何防止在 ExpressJS 中重复提交表单
How to prevent double submitting forms in ExpressJS
我遇到了我网站上的人可以重复提交表单的问题。这不是数据库端的问题,因为记录是通过服务器端验证保存的。但是,重要的是我使任何其他请求无效,这样用户就不会被重复收费(因为收费发生在创建帐户之前)。我宁愿完全放弃请求而不是重定向,因为它真的会破坏用户体验,而放弃请求只会使过程 "appear" 更长,这是一个更好的折衷方案。
为了弄清楚,我也使用了post-redirect-get 模式,但它并没有解决这个问题,这实际上是最关键的问题,因为它是最常见的。为了 UI 的清洁,将实施客户端解决方案,但我不能相信用户不会禁用 javascript 或以某种方式篡改它。
我有一个使用库 csurf
的中间件解决方案,但效果不佳。
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);
};
我像这样将它附加到我的路线上:
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.
});
不幸的是,即使调用了中间件,如果我按下创建按钮,它也会触发大量请求,每个请求都会尝试将令牌推送到 unique_requests
,结果是它赢了'阻止任何推送的请求,直到一切都赶上。
我在这里做错了什么,我该如何解决?
编辑: 将 POST 的速率限制为 /create 似乎是一种可能性,尽管它看起来不像是 真实的 我想是解决方案。可能有用。
您应该能够通过使用这些中间件处理标志来简化它,如下所示:
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();
};
by using a simple session counter and set it as needed in this example of
adding a role name single field string unique in schema definition
counter field name is rolesubmitcounter
///////////////////////////////////////////
router.get('/add',ensureAuthenticated, function(req,res){
req.session.rolesubmitcounter=0;
res.render('roles/role_add',{
title:"add role",
nrole: {roleName:""}
});
});
// Add Submit POST Route
router.post('/add', ensureAuthenticated, function(req, res){
if (req.session.rolesubmitcounter ===0){
var tt1 =req.body.roleName.toString().trim();
if(tt1==="".trim()){
req.session.rolesubmitcounter=0;
req.flash('danger','Role not..added.....may be empty fields......');
res.render('roles/role_add', {
title:'Add role',
nrole: {roleName:req.body.roleName}
});
}
else{
///////////////////////////////////////
req.session.rolesubmitcounter = 1;
let nrole = new Role();
nrole.roleName = req.body.roleName.toLowerCase().trim();
///////////////////////////////////////////
nrole.save(function(err){
if(err){
req.session.rolesubmitcounter=0;
req.flash('danger','Role not..added.....may be duplicate key..');
res.render('roles/role_add',{
nrole: nrole
});
} else {
req.flash('success','Role Added');
res.redirect('/roles/roleindex');
}
});
}
}else{
req.flash('danger','double submission Detected.....click once slowly');
res.redirect('/roles/roleindex');
}
});
我遇到了我网站上的人可以重复提交表单的问题。这不是数据库端的问题,因为记录是通过服务器端验证保存的。但是,重要的是我使任何其他请求无效,这样用户就不会被重复收费(因为收费发生在创建帐户之前)。我宁愿完全放弃请求而不是重定向,因为它真的会破坏用户体验,而放弃请求只会使过程 "appear" 更长,这是一个更好的折衷方案。
为了弄清楚,我也使用了post-redirect-get 模式,但它并没有解决这个问题,这实际上是最关键的问题,因为它是最常见的。为了 UI 的清洁,将实施客户端解决方案,但我不能相信用户不会禁用 javascript 或以某种方式篡改它。
我有一个使用库 csurf
的中间件解决方案,但效果不佳。
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);
};
我像这样将它附加到我的路线上:
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.
});
不幸的是,即使调用了中间件,如果我按下创建按钮,它也会触发大量请求,每个请求都会尝试将令牌推送到 unique_requests
,结果是它赢了'阻止任何推送的请求,直到一切都赶上。
我在这里做错了什么,我该如何解决?
编辑: 将 POST 的速率限制为 /create 似乎是一种可能性,尽管它看起来不像是 真实的 我想是解决方案。可能有用。
您应该能够通过使用这些中间件处理标志来简化它,如下所示:
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();
};
by using a simple session counter and set it as needed in this example of
adding a role name single field string unique in schema definition
counter field name is rolesubmitcounter
///////////////////////////////////////////
router.get('/add',ensureAuthenticated, function(req,res){
req.session.rolesubmitcounter=0;
res.render('roles/role_add',{
title:"add role",
nrole: {roleName:""}
});
});
// Add Submit POST Route
router.post('/add', ensureAuthenticated, function(req, res){
if (req.session.rolesubmitcounter ===0){
var tt1 =req.body.roleName.toString().trim();
if(tt1==="".trim()){
req.session.rolesubmitcounter=0;
req.flash('danger','Role not..added.....may be empty fields......');
res.render('roles/role_add', {
title:'Add role',
nrole: {roleName:req.body.roleName}
});
}
else{
///////////////////////////////////////
req.session.rolesubmitcounter = 1;
let nrole = new Role();
nrole.roleName = req.body.roleName.toLowerCase().trim();
///////////////////////////////////////////
nrole.save(function(err){
if(err){
req.session.rolesubmitcounter=0;
req.flash('danger','Role not..added.....may be duplicate key..');
res.render('roles/role_add',{
nrole: nrole
});
} else {
req.flash('success','Role Added');
res.redirect('/roles/roleindex');
}
});
}
}else{
req.flash('danger','double submission Detected.....click once slowly');
res.redirect('/roles/roleindex');
}
});