8

想象一个带有路由的 Web 应用程序,在继续之前需要检查用户是否被允许访问给定的资源。“已验证”检查依赖于数据库调用。

在每条路线中,我可能有:

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())会变得更加复杂,因为它必须处理错误(使用 a.catch()或使用nullcheck in .then())。

不过有两件事让我有点紧张:

  • authorizeOwnership()在错误场景中永远不会解决的承诺是否可以?它会导致延迟或内存泄漏吗?

  • confirmOwnership()用 null 解决说“找不到匹配的资源”然后将其视为错误在逻辑上是否合理authorizeOwnership()?我的第一次尝试在没有匹配资源的情况下拒绝了承诺confirmOwnership(),但这使事情变得更加复杂,因为很难区分这个(403 案例)和实际错误(500 案例)。

4

1 回答 1

5

authorizeOwnership() 返回的承诺在错误情况下永远不会被解决是否可以?它会导致延迟或内存泄漏吗?

是的,不解决 Bluebird 承诺是安全的(公平地说,我检查过的任何其他实现 -这里有漂亮的图片)。它本身没有全局状态。

它是否是好的做法的问题是不同的。从某种意义上说,它就像一个同步break。就个人而言,我不是粉丝。

用 null 解析 confirmOwnership() 说“没有找到匹配的资源”然后将其视为 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(...){,你可以简单地返回承诺。

于 2014-04-12T23:12:19.200 回答