想象一个Web应用程序,其路由需要检查是否允许用户在继续之前访问给定资源。 "经过身份验证" check依赖于数据库调用。
在每条路线中,我可能有:
authorizeOwnership(req, res)
.then(function() {
// do stuff
res.send(200, "Yay");
});
我希望authorizeOwnership()
函数处理403(拒绝访问)和500(例如数据库查询错误)响应,以便每条路由都不需要明确地执行此操作。
我有一个可以查询数据库以检查所有权的函数。
function confirmOwnership(resourceId, userId) {
// SequelizeJS... returns a bluebird promise
return Resource.find({
where: {id: resourceId, userId: userId}
})
.then(function(resource) {
if(!resource) {
return null; // no match for this resource id + user id
} else {
return resource;
}
});
}
然后在authorizeOwnership
:
function authorizeOwnership(req, res) {
var rid = parseInt(req.params.rid, 10),
userId = parseInt(req.authInfo.userid, 10);
return new Promise(function(resolve, reject) {
confirmOwnership(rid, userId)
.then(function(resource) {
if(resource === null) {
res.send(403, "Forbidden");
// Note: we don't resolve; outer handler will not be called
} else {
resolve(resource);
}
})
.catch(function(err) {
console.log(err);
res.send(500, "Server error");
// Note: we don't resolve; outer handler will not be called
});
});
}
在这种情况下,我故意不在某些代码路径中调用reject()
或resolve()
。如果我这样做,那么我的"外部"路由逻辑(调用authorizeOwnership()
的代码)变得更复杂,因为它必须处理错误(使用.catch()
或null
检查.then()
)。
但有两件事让我有些紧张:
authorizeOwnership()
返回的承诺在错误情况下永远无法解决吗?它会导致延迟还是内存泄漏?
在解析confirmOwnership()
时使用null来解释"找不到匹配的资源"是否合乎逻辑?然后将其视为authorizeOwnership()
中的错误?当没有匹配资源时,我的第一次尝试拒绝了confirmOwnership()
中的承诺,但这使事情变得更复杂,因为很难区分这种情况(403情况)和实际错误(500情况)。
答案 0 :(得分:5)
authorizeOwnership()返回的promise在错误情况下永远无法解决吗?它会导致延迟还是内存泄漏?
是的,不解决蓝鸟承诺是安全的(公平地说,我已经检查过的任何其他实施 - pretty pictures here)。它本身没有全球状态。
它的良好实践是否不同的问题。从某种意义上说,它就像是同步break
。就个人而言,我不是粉丝。
使用null解析confirmOwnership()在逻辑上听起来是否正确#34;找不到匹配的资源"然后将其视为authorizeOwnership()中的错误?
这取决于您的API。它再次成为一种观点。它可以工作,但我可能不会返回null,但如果情况例外,则表示失败。您可以使用错误对象区分拒绝。例如,您可以使用您创建的AuthorizationError
对象拒绝。注意Bluebird还支持类型化捕获。
类似的东西:
// probably shouldn't send the response to authorizeOwnership but use it externally
// to be fair, should probably not take req either, but rid and userid
var authorizeOwnership = Promise.method(function(req) {
var rid = Number(req.params.rid),
userId = Number(req.authInfo.userid;
return confirmOwnership(rid, userId); // return the promise
});
});
function ServerError(code,reason){
this.name = "ServerError";
this.message = reason;
this.code = code;
Error.captureStackTrace(this); // capture stack
}
var confirmOwnership = Promise.method(function(resourceId, userId) {
// SequelizeJS... returns a bluebird promise
return Resource.find({
where: {id: resourceId, userId: userId}
})
.then(function(resource) {
if(!resource) {
throw new ServerError(403,"User not owner"); // promises are throw safe
}
return resource;
});
});
接下来,在您的服务器中,您可以执行以下操作:
app.post("/foo",function(req,res){
authorizeOwnership(req).then(function(){
res.send(200, "Owner Yay!");
}).catch(ServerError,function(e){
if(e.code === 403) return res.send(403,e.message);
return res.send(500,"Internal Server Error");
});
});
注意:您还在代码中使用了延迟反模式。您无需在代码中执行new Promise(function(...){
,只需返回承诺即可。