使用 ES6 promises 和 BookshelfJS 捕获错误

Catching errors with ES6 promises and BookshelfJS

我正在向 ExpressJS 应用程序中的 Bookshelf 用户模型添加一个非常基本的登录方法,但我无法从用户模型 returns 中的登录功能被拒绝的承诺中捕获错误.我正在 http://bookshelfjs.org/#Model-static-extend 的文档中查看 Bookshelf 的登录示例,但该示例使用的是 Bluebird,而我正在尝试对内置的 ES6 promises 做同样的事情。

我在用户模型中的登录方法:

userModel.js

function login(email, password) {
  return new Promise((resolve, reject) => {
    User.where('email', email)
      .fetch({ require: true })
      .then(user => {
        bcrypt.compare(password, user.get('password'), (err, matched) => {
          if (!matched) return reject(new Error('Password didn\'t match!'));
          resolve(user);
        });
      });
  });

从书架User模型实现登录和调用User.login的控制器动作:

usersAuthController.js

function logUserIn(req, res) {
  new User().login(req.body.email, req.body.password)
    .then(user => res.json({ message: 'Login succeeded!' }))
    .catch(User.NotFoundError, () => res.status(404).json({ error: 'User not found!' }) // catch #1
    .catch(err => res.status(401).json({ err: err.message })); // catch #2
}

我的意图是当 Bookshelf 的 User.fetch 方法找不到具有给定电子邮件的用户时,login() 可以 return 拒绝承诺。在那种情况下,.catch(User.NotFoundError ...) (catch #1) 行应该捕获它并且 return 一个 404。我还打算 login() 到 return 当 bcrypt 确定传递给 login() 的密码与用户的密码不匹配,在这种情况下 User.NotFoundError catch 语句下面的 "catch-all" (捕获 #2)应该 return a 401.

如果我输入的密码不正确,上面代码中的 logUserIn() 控制器操作会使用错误消息 { error: "Cannot set property 'message' of undefined" } 而不是消息 'Password didn't match!' 来捕获 #2我在 login() 中拒绝了。如果我输入一个不存在的电子邮件,则永远不会发送响应,并且会在控制台中抛出错误 Unhandled rejection CustomError: EmptyResponse。只有有效的输入才有效。

尝试修复: 直接在模型中捕获 User.NotFoundError

我将第一个问题移到了用户模型中,这样登录方法现在看起来像:

userModel.js

function login(email, password) {
  return new Promise((resolve, reject) => {
    User.where('email', email)
      .fetch({ require: true })
      .then(user => {
        bcrypt.compare(password, user.get('password'), (err, matched) => {
          if (!matched) return reject(new Error('Password didn\'t match!'));
          resolve(user);
        });
      })
      .catch(User.NotFoundError, () => reject({ error: 'User not found!' }));
  });

通过这种方式,我可以正确捕获这两个错误(密码不正确和电子邮件不存在),但是这样我无法在控制器中指定状态代码。如果找不到具有给定电子邮件的用户,那么它应该 return 一个 404,但是如果密码不正确,那么它应该 return 一个 401,但是这两个错误都会出现 -控制器操作中的所有(捕获#2)(始终 returns a 401)。

为了解决这个问题,在 User 模型中我可以做 .catch(User.NotFoundError, () => reject({ name: 'NotFoundError', message: 'User not found!' })) 并且在控制器操作中我可以检查 const statusCode = err.name === 'NotFoundError' ? 404 : 401 我遇到了什么样的错误但是这看起来真的很乱,并且错过了这些 .catch 语句的要点。

有没有办法从模型的登录方法中捕获 User.NotFoundError 以及 logInUser 中的任何其他错误?为什么我一开始的设置不起作用,usersAuthController.js 中有两个 catch 语句,Cannot set property 'message' of undefined'CustomError: EmptyResponse 错误意味着什么(它有与混淆 Bookshelf 的 Bluebird 承诺与内置 ES6 承诺有关)?处理此问题的最佳方法是什么?

在您的初始实施中,您没有传播任何可能由 User.fetch() 引起的拒绝。此外,因为 User.fetch() 已经 returns 一个承诺,用一个新的承诺包装它有点反模式(尽管你仍然需要用一个承诺包装 bcrypt.compare(),因为那只是与回调一起工作)。

试试这个:

function login(email, password) {
  return User .where('email', email)
              .fetch({ require: true })
              .then(user => {
                return new Promise((resolve, reject) => {
                  bcrypt.compare(password, user.get('password'), (err, matched) => {
                    if (err)      return reject(err);
                    if (!matched) return reject(new Error('Password didn\'t match!'));
                    resolve(user);
                  });
                })
              });
}