不解决或拒绝诺言是否安全? [英] Is it safe to not resolve or reject a promise

查看:98
本文介绍了不解决或拒绝诺言是否安全?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

想象一下一个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()的代码)将变得更加复杂,因为它必须处理错误(使用.catch()或使用null检入.then())./p>

两件事让我有点紧张:

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

  • 从逻辑上讲,将null解析为confirmOwnership()时说找不到匹配的资源",然后将其视为authorizeOwnership()中的错误,这是否合理?当没有匹配的资源时,我的第一次尝试就拒绝了confirmOwnership()中的诺言,但这使事情变得更加复杂,因为很难区分此错误(403例)和实际错误(500例).

解决方案

在错误情况下永远不能解决authorizeOwnership()返回的诺言吗?它会导致延迟或内存泄漏吗?

是的,不解决Bluebird承诺是安全的(并且公平地说,我检查过的任何其他实现-

这取决于您的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(...){,您只需返回承诺即可.

Imagine a web application with routes that need to check whether the user is allowed to access a given resource before proceeding. The "is authenticated" check relies on a database call.

In each route, I may have:

authorizeOwnership(req, res)
.then(function() {
    // do stuff
    res.send(200, "Yay");
});

I want the authorizeOwnership() function to handle 403 (access denied) and 500 (e.g. database query error) responses so that each route doesn't need to do so explicitly.

I have a function that can query the database to check for ownership.

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;
        }
    });
}

This is then used in 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
        });
    });
}

In this scenario, I deliberately don't call reject() or resolve() in some of the code paths. If I do, then my "outer" route logic (the code that's calling authorizeOwnership()) gets more complicated because it has to handle errors (either with a .catch() or with a null check in .then()).

Two things make me a bit nervous though:

  • Is it OK for the promise returned by authorizeOwnership() to never be resolved in error scenarios? Will it cause a delay or a memory leak?

  • Is it logically sound to resolve confirmOwnership() with null to say "no matching resource found" and then treat that as an error in authorizeOwnership()? My first attempt rejected the promise in confirmOwnership() when there was no matching resource, but this made things more complicated because it is difficult to distinguish between this (the 403 case) and an actual error (the 500 case).

解决方案

Is it OK for the promise returned by authorizeOwnership() to never be resolved in error scenarios? Will it cause a delay or a memory leak?

Yes, it is safe to not resolve a Bluebird promise (and to be fair, any other implementation I've checked -pretty pictures here). It has no global state on its own.

The question of whether or not it's good practice is different. It's like a synchronous break in a sense. Personally I'm not a fan.

Is it logically sound to resolve confirmOwnership() with null to say "no matching resource found" and then treat that as an error in authorizeOwnership()?

This depends on your API. It's again an opinion. It works, but I probably would not return null but indicate failure if the case is exceptional. You can distinguish the rejection using an error object. You can reject with an AuthorizationError object you create for example. Note Bluebird also supports typed catches.

Something like:

// 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;
    });
});

Next, in your server, you can do something like:

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");
     });
});

Note: you're also using the deferred anti pattern in your code. There is no need to do new Promise(function(...){ in your code, you can simply return the promise.

这篇关于不解决或拒绝诺言是否安全?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆