Express.js和Bluebird - 处理承诺链 [英] Express.js and Bluebird - Handling the promise chain

查看:111
本文介绍了Express.js和Bluebird - 处理承诺链的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In a backend API I have a login route which should perform the following sequence of actions:


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

  • Given an username and password, try to authenticate the user against an Active Directory. If authentication has failed reply with status 401. If success, continue.

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

Look for an user with the given username in the database. If not found reply with status 403, otherwise continue.

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

Find if the user document has some details like email, display name, etc (in case this is not the first time logging in). If yes reply with the user object, otherwise continue.

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

Get user details from the Active Directory and update the user object in the database. Reply with the updated object.

代码:

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的承诺,但我有两个问题:

Now I had this running with callbacks but it was really nested. So I wanted to give Bluebird promises a try, but I have two problems:


  • 看起来很乱,任何更好的方法来链接调用和处理响应?

  • Looks chaotic, any better way to chain the calls and handle responses?

每当我回复 next()以在回复后停止请求时,继续执行另一个 .then()。虽然客户端收到正确的响应,但在服务器日志中我发现执行仍在继续。例如,如果给定用户的DB中没有帐户,则客户端会收到 403 响应,但在服务器日志中,我看到异常失败读取属性displayName为null ,因为没有用户,它应该在 res之后停在 next()中.status(403).send();

Whenever I call next() to stop the request after replying, the execution continues to the other .then(). Although the client receives the correct response, in the server log I find that the execution have continued. For example, if there is no account in DB for a given user, the client receives the 403 response but in the server log I see an exception failed to read property displayName of null, because there was no user and it should have stopped in the next() after res.status(403).send();.

推荐答案

最佳使用 if / else 以明确哪些分支将执行,哪些不会:

Best use if/else to make clear what branches will execute and which won't:

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

嵌套然后调用是非常必要的对于条件评估,你不能将它们串联起来并在中间突破(除了抛出异常,这真的很难看)。

The nesting of then calls is quite necessary for conditional evaluation, you cannot chain them linearly and "break out" in the middle (other than by throwing exceptions, which is really ugly).

如果你不这样做像所有那些然后回调一样,你可以使用 async / await 语法(可能使用转换器 - 或使用Bluebird的 Promise.coroutine 使用生成器语法模拟它。然后你的整个代码变成

If you don't like all those then callbacks, you can use async/await syntax (possibly with a transpiler - or use Bluebird's Promise.coroutine to emulate it with generator syntax). Your whole code then becomes

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

这篇关于Express.js和Bluebird - 处理承诺链的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆