不解决或拒绝承诺是否安全

时间:2014-04-12 23:03:30

标签: promise bluebird

想象一个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情况)。

1 个答案:

答案 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(...){,只需返回承诺即可。