Javascript Express.js和Bluebird-处理承诺链
在后端API中,我有一个登录路径,该路径应执行以下操作序列:Javascript Express.js和Bluebird-处理承诺链,javascript,node.js,express,promise,bluebird,Javascript,Node.js,Express,Promise,Bluebird,在后端API中,我有一个登录路径,该路径应执行以下操作序列: 给定用户名和密码,尝试根据Active Directory对用户进行身份验证。如果身份验证失败,则回复状态为401。如果成功,请继续 在数据库中查找具有给定用户名的用户。如果未找到,则回复状态为403,否则继续 查找用户文档是否包含一些详细信息,如电子邮件、显示名称等(以防这不是第一次登录)。如果是,用用户对象回复,否则继续 从Active Directory获取用户详细信息并更新数据库中的用户对象。用更新的对象回复 代码: 现在
- 给定用户名和密码,尝试根据Active Directory对用户进行身份验证。如果身份验证失败,则回复状态为401。如果成功,请继续
- 在数据库中查找具有给定用户名的用户。如果未找到,则回复状态为403,否则继续
- 查找用户文档是否包含一些详细信息,如电子邮件、显示名称等(以防这不是第一次登录)。如果是,用用户对象回复,否则继续
- 从Active Directory获取用户详细信息并更新数据库中的用户对象。用更新的对象回复
- 看起来很混乱,有没有更好的方法来连接电话和处理回应
- 每当我在应答后调用
停止请求时,执行将继续到另一个next()
。尽管客户端收到了正确的响应,但在服务器日志中,我发现执行仍在继续。例如,如果给定用户的数据库中没有帐户,客户端将收到。然后()
响应,但在服务器日志中,我看到异常403
,因为没有用户,它应该在无法读取null的属性displayName
res.status(403).send()之后在
中停止代码>下一个()
if
/else
来明确哪些分支将执行,哪些不执行:
ad.authenticate(username, password).then((success) => {
if (!success) {
res.status(401).send(); // authentication failed
} else {
return User.findOne({ username }).exec().then(user => {
if (!user) {
res.status(403).send(); // unauthorized, no account in DB
} else if (user.displayName) {
res.status(201).json(user); // all good, return user details
} else {
// fetch user details from the AD
return ad.getUserDetails(username, password).then(details => {
// update user object with the response details and save
// ...
return user.save();
}).then(update => {
res.status(201).json(update); // all good, return user object
});
}
});
}
}).then(() => next(), err => next(err));
嵌套的<>代码> 调用对于条件评估是非常必要的,不能将它们线性地链接起来,并在中间抛出(除了抛出异常,这非常丑陋)。 如果您不喜欢所有这些
,那么
回调,您可以使用异步
/等待
语法(可能使用transpiler,或者使用Bluebird的生成器语法来模拟它)。然后,您的整个代码将变为
router.post('/login', async (req, res, next) => {
try {
// authenticate
const success = await ad.authenticate(req.body.username, req.body.password);
if (!success) {
res.status(401).send(); // authentication failed
} else {
const user = await User.findOne({ username }).exec();
if (!user) {
res.status(403).send(); // unauthorized, no account in DB
} else if (user.displayName) {
res.status(201).json(user); // all good, return user details
} else {
// fetch user details from the AD
const details = await ad.getUserDetails(username, password);
// update user object with the response details and save
// ...
const update = await user.save();
res.status(201).json(update); // all good, return user object
}
}
next(); // let's hope this doesn't throw
} catch(err) {
next(err);
}
});
要回答第二点,您必须在调用
next()
后拒绝您的承诺(或者至少返回一些内容,否则将执行后面的行)。差不多
next();
return Promise.reject()
并更改捕获,以便在没有错误的情况下工作
.catch(err => {
if (err)
next(err)
});
首先回答第二个问题:除非回调抛出类似错误,否则没有办法打破/停止承诺链
doAsync()
.then(()=>{
throw 'sth wrong'
})
.then(()=>{
// code here never runs
})
您可以在下面的演示中尝试验证第二个回调是否仍在运行
doAsync()
.then(()=>{
res.end('end')
})
.then(()=>{
// code here always runs
})
doAsync()
.then(()=>{
return;
})
.then(()=>{
// code here always runs
})
对于第一个问题:使用then()中的第二个参数,表示拒绝。每次都把逻辑分成两部分
var p = new Promise(function(resolve, reject) {
return
ad.auth(username, password).then(()={
// check if 401 needed. If needed, return reject
if (dont needed 401 in your logic)
resolve(username)
else
reject({ msg: 'authentication has failed', status: 401 })
})
});
p
.then( (username)=>{
// this only runs when the previous resolves
return User.findOne({ username }).exec()
}, (data)=>{
// in fact in your case you dont even have to have the reject callback
return data
} )
.then( (found)=>{
return
new Promise(function(resolve, reject) {
if (found && /*your logic to determine it's not 403*/)
resolve(user)
else
reject({ msg: 'unauthorized, no account in DB', status: 403 })
})
} )
.then( (found)=>{
return
new Promise(function(resolve, reject) {
if (found && /*your logic to determine it's not 403*/)
resolve(user)
else
reject({ msg: 'unauthorized, no account in DB', status: 403 })
})
} )
.then( (user)=>{
return
new Promise(function(resolve, reject) {
if (/*your logic to determine it has the full info*/)
resolve(user)
else
return ad.getUserDetails(username, password)
})
} )
.then( (user)=>{
// all is good, do the good logic
}, (data)=>{
// something wrong, so here you can handle all the reject in one place
res.send(data)
} )
我目前使用的是最新的节点版本7,并且启用了
--harmony async await
,然后您可以使用async/await模式,这确实可以整理代码。您必须返回下一步()
可能我误解了,但是OP如果调用下一步
,就不想再执行。因此,一种方法是拒绝承诺(无论有无错误),感谢编辑。没有yourErrorHere
,只有在真正发生错误时才调用next(err)
,这是我在第一次修订中遗漏的两件重要事情。这使得事情更加复杂。不过,谢谢你详细的回答.then(…,data=>{return data;})
肯定做不到您想做的事。无效!您的代码中绝对不需要任何newpromise
调用。谢谢@Bergi,如果不使用newpromise()
,如何实现拒绝部分?你可以做一些同步检查,看看承诺应该是拒绝还是解决。请检查,@João Pereira,答案是你想要的吗?太棒了。Async/await似乎与promisifyed库配合得很好,就是这样。是的,如果router.post
是promise-await-await-await-await-await-await-await-await-await-await-await-await-await-await-await-await-await-await-Awa
var p = new Promise(function(resolve, reject) {
return
ad.auth(username, password).then(()={
// check if 401 needed. If needed, return reject
if (dont needed 401 in your logic)
resolve(username)
else
reject({ msg: 'authentication has failed', status: 401 })
})
});
p
.then( (username)=>{
// this only runs when the previous resolves
return User.findOne({ username }).exec()
}, (data)=>{
// in fact in your case you dont even have to have the reject callback
return data
} )
.then( (found)=>{
return
new Promise(function(resolve, reject) {
if (found && /*your logic to determine it's not 403*/)
resolve(user)
else
reject({ msg: 'unauthorized, no account in DB', status: 403 })
})
} )
.then( (found)=>{
return
new Promise(function(resolve, reject) {
if (found && /*your logic to determine it's not 403*/)
resolve(user)
else
reject({ msg: 'unauthorized, no account in DB', status: 403 })
})
} )
.then( (user)=>{
return
new Promise(function(resolve, reject) {
if (/*your logic to determine it has the full info*/)
resolve(user)
else
return ad.getUserDetails(username, password)
})
} )
.then( (user)=>{
// all is good, do the good logic
}, (data)=>{
// something wrong, so here you can handle all the reject in one place
res.send(data)
} )