【发布时间】:2016-07-16 07:08:59
【问题描述】:
我正在向 ExpressJS 应用程序中的 Bookshelf User 模型添加一个非常基本的登录方法,但我无法从用户模型中的登录函数返回的被拒绝承诺中捕获错误。我在 http://bookshelfjs.org/#Model-static-extend 的文档中查看 Bookshelf 的登录示例,但该示例使用 Bluebird,而我正在尝试对内置的 ES6 承诺做同样的事情。
我在 User 模型中的登录方式:
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() 可以返回拒绝的承诺。在这种情况下,.catch(User.NotFoundError ...) (catch #1) 行应该捕获它并返回 404。我还打算在 bcrypt 确定传递给 login() 的密码不匹配时,login() 返回一个被拒绝的 Promise用户的密码,在这种情况下,User.NotFoundErrorcatch 语句下方的“catch-all”(catch #2)应返回 401。
如果我输入了不正确的密码,上述代码中的logUserIn() 控制器操作将使用错误消息{ error: "Cannot set property 'message' of undefined" } 捕获#2,而不是我在login() 中拒绝的消息'Password didn't match!' 消息。如果我输入不存在的电子邮件,则永远不会发送响应,并且在控制台中会抛出错误 Unhandled rejection CustomError: EmptyResponse。只有有效的输入有效。
尝试修复:直接在模型中捕获User.NotFoundError。
我将 catch #1 移到了 User 模型中,因此登录方法现在看起来像:
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!' }));
});
这样,我可以正确捕获两个错误(密码错误和不存在的电子邮件),但这样我无法在控制器中指定状态代码。如果找不到具有给定电子邮件的用户,则应返回 404,但如果密码不正确,则应返回 401,但两个错误都会出现在控制器操作(始终返回 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 承诺)?处理这个问题的最佳方法是什么?
【问题讨论】:
-
避免
Promiseconstructor antipattern!您应该只在login函数中承诺bcrypt.compare。
标签: javascript node.js express es6-promise bookshelf.js