javascript - Express.js 和 Bluebird - 处理 promise 链

标签 javascript node.js express promise bluebird

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

  • 给定用户名和密码,尝试根据 Active Directory 对用户进行身份验证。如果身份验证失败,回复状态 401。如果成功,则继续。

  • 在数据库中查找具有给定用户名的用户。如果未找到状态为 403 的回复,否则继续。

  • 查找用户文档是否包含一些详细信息,如电子邮件、显示名称等(以防这不是第一次登录)。如果是,用用户对象回复,否则继续。

  • 从 Active Directory 获取用户详细信息并更新数据库中的用户对象。回复更新后的对象。

代码:

router.post('/login', (req, res, next) => {

  // capture credentials
  const username = req.body.username;
  const password = req.body.password;
  let user = null;

  // authenticate
  ad.authenticate(username, password)
    .then((success) => {
      if (!success) {
        res.status(401).send(); // authentication failed
        next();
      }
      return User.findOne({ username }).exec();
    })

    .then((found) => {
      if (!found) {
        res.status(403).send(); // unauthorized, no account in DB
        next();
      }
      user = found;
      if (user.displayName) {
        res.status(201).json(user); // all good, return user details
        next();
      }
      // 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
      next();
    })

    .catch(err => next(err));

});

现在我用回调运行了它,但它确实是嵌套的。所以我想尝试一下 Bluebird 的 promise ,但我有两个问题:

  • 看起来很困惑,有更好的方法来链接调用和处理响应吗?

  • 每当我在回复后调用 next() 停止请求时,执行会继续到另一个 .then()。尽管客户端收到了正确的响应,但在服务器日志中我发现执行仍在继续。例如,如果给定用户在数据库中没有帐户,客户端会收到 403 响应,但在服务器日志中我看到异常 failed to read property displayName of null,因为没有用户,它应该在 res.status(403).send(); 之后的 next() 中停止。

    <

最佳答案

最好使用 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));

then 调用的嵌套对于条件评估是非常必要的,你不能将它们线性链接并在中间“中断”(除了抛出异常,这真的很丑陋)。

如果您不喜欢所有这些 then 回调,您可以使用 async/await 语法(可能使用转译器 - 或者使用Bluebird 的 Promise.coroutine 用生成器语法模拟它)。你的整个代码就变成了

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);
  }
});

关于javascript - Express.js 和 Bluebird - 处理 promise 链,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40679021/

相关文章:

javascript - Node.js 异步系列无法按顺序工作

node.js - 如何将 AWS Cognito 子 UUID 添加到 Sequelize 表作为主键?

javascript - node和node中间件中的 'req'和 'res'参数是什么?

javascript - 单击 Javascript 中的下一步按钮后,上一个 View 不会消失

javascript - OpenLayers:如何在标记上添加点击和触摸事件

JavaScript 电子邮件正则表达式

javascript - 在 Nodejs 中使用 ssh2 传输整个目录

javascript - axios.get() 请求返回未定义

angularjs - socket.io 手动将用户添加到房间(node.js)

javascript - 从对象数组中获取最后一个匹配的键来检查值