Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/460.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/38.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript Express.js和Bluebird-处理承诺链_Javascript_Node.js_Express_Promise_Bluebird - Fatal编程技术网

Javascript Express.js和Bluebird-处理承诺链

Javascript Express.js和Bluebird-处理承诺链,javascript,node.js,express,promise,bluebird,Javascript,Node.js,Express,Promise,Bluebird,在后端API中,我有一个登录路径,该路径应执行以下操作序列: 给定用户名和密码,尝试根据Active Directory对用户进行身份验证。如果身份验证失败,则回复状态为401。如果成功,请继续 在数据库中查找具有给定用户名的用户。如果未找到,则回复状态为403,否则继续 查找用户文档是否包含一些详细信息,如电子邮件、显示名称等(以防这不是第一次登录)。如果是,用用户对象回复,否则继续 从Active Directory获取用户详细信息并更新数据库中的用户对象。用更新的对象回复 代码: 现在

在后端API中,我有一个登录路径,该路径应执行以下操作序列:

  • 给定用户名和密码,尝试根据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) 
} )