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()orを呼び出しません。その場合、エラーを処理する必要があるため (またはcheck inを使用して) resolve()、「外部」ルート ロジック ( を呼び出しているコードauthorizeOwnership()) がより複雑になります。.catch()null.then()

ただし、次の 2 つの点で少し緊張します。

  • によって返された promiseauthorizeOwnership()がエラー シナリオで解決されなくても問題ありませんか? 遅延やメモリリークの原因になりますか?

  • confirmOwnership()「一致するリソースが見つかりません」と言ってnullで解決し、それをエラーとして扱うのは論理的に正しいauthorizeOwnership()ですか? 私の最初の試行では、一致するリソースがない場合に promise を拒否しましconfirmOwnership()たが、これ (403 のケース) と実際のエラー (500 のケース) を区別するのが難しいため、事態はより複雑になりました。

4

1 に答える 1

5

エラー シナリオで authorizeOwnership() によって返された promise が解決されなくても問題ありませんか? 遅延やメモリリークの原因になりますか?

はい、Bluebird の promise を解決しないことは安全です (公平を期すために、私がチェックした他の実装 -きれいな写真はこちら)。それ自体にはグローバルな状態はありません。

それが良い習慣であるかどうかの問題は異なります。breakある意味シンクロのようなものです。個人的に私はファンではありません。

confirmOwnership() を null で解決して「一致するリソースが見つかりません」と言って、それを 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");
     });
});

注: コードで deferred アンチ パターンも使用しています。コードで行う必要はありませんnew Promise(function(...){。単に promise を返すことができます。

于 2014-04-12T23:12:19.200 に答える