web-dev-qa-db-ja.com

「ローカルでキャッチされた例外の「スロー」を修正する方法は?

REST API呼び出しを処理するこの関数では、要求の一部を処理するために呼び出された関数がエラーをスローして、エラーコードを応答として送信する必要があることを通知します。それ自体がエラーを発見する場合もあり、その時点で例外処理ブロックにジャンプする必要があります。

static async handleRequest(req) {
    try {
        let isAllowed = await checkIfIsAllowed(req);
        if (!isAllowed) {
            throw new ForbiddenException("You're not allowed to do that.");
        }
        let result = await doSomething(req); // can also raise exceptions
        sendResult(result);
    } catch(err) {
        sendErrorCode(err);
    }
}

Webstormは、次のメッセージでthrowに下線を付けます:'throw' of exception caught locally. This inspection reports any instances of JavaScript throw statements whose exceptions are always caught by containing try statements. Using throw statements as a "goto" to change the local flow of control is likely to be confusing.

ただし、状況を改善するためにコードをリファクタリングする方法がわかりません。

catchブロックからifチェックにコードをコピーして貼り付けることはできますが、これによりコードが読みにくくなり、保守が難しくなると思います。

isAllowedチェックを行い、成功しない場合は例外をスローする新しい関数を作成できますが、Webstormが報告していると思われる設計上の問題を修正するのではなく、問題を回避しているようです。

私たちは例外を悪い方法で使用していますか、それがこの問題に遭遇している理由ですか、それともWebstormエラーは単純に誤解されて無効にすべきですか?

19
cib

isAllowedが失敗した場合、何かをチェックして例外をスローしていますが、その状況で何をすべきかはわかっています-sendErrorCodeを呼び出します。状況の処理方法がわからない場合(例外的な状況など)、外部の呼び出し元に例外をスローする必要があります。

この場合、これが発生した場合に何をすべきかの定義済みプロセスが既にあります-間接的なスロー/キャッチなしで直接使用するだけです:

static async handleRequest(req) {
    try {
        let isAllowed = await checkIfIsAllowed(req);
        if (!isAllowed) {
            sendErrorCode("You're not allowed to do that.");
            return;
        }
        let result = await doSomething(req); // can also raise exceptions
        sendResult(result);
    } catch(err) {
        sendErrorCode(err);
    }
}

コードをcatchブロックからifcheckにコピーして貼り付けることもできますが、これによりコードが読みにくくなり、保守が難しくなると思います。

それどころか、上記のように、私はこれがこの状況に対処する方法になると期待しています。

25
James Thorpe

これはブロッキングエラーではなく、IDEの推奨事項のみであるため、質問は両側から表示する必要があります。

最初の側面はパフォーマンスです。これがボトルネックであり、コンパイルで使用したり、nodejsの新しい(まだリリースされていない)バージョンに転送するときに使用できる可能性がある場合、繰り返しの存在は必ずしも悪い解決策ではありません。この場合、IDEヒントは正確にヒントを示し、そのような設計は場合によっては最適化が不十分になる可能性があるようです。

2番目の側面はコード設計です。コードを読みやすくし、他の開発者の作業を簡素化する場合は、そのままにしてください。この観点から、ソリューションはすでに上で提案されています。

1
SlowSuperman

「例外を通常のフロー制御として使用しないのはなぜですか」という質問に対する良い答えがあります。 ここ

ローカルでキャッチする例外をスローしない理由は、その状況の処理方法をローカルで知っているためです。したがって、定義上、例外ではありません。

@James Thorpeの answer は良さそうに見えますが、@ matchishはDRYに違反していると感じています。一般的に言って、そうではありません。 DRY(Do n't Repeat Yourselfの略)は、「すべての知識がシステム内で単一の明確な、権威ある表現を持たなければならない」というフレーズを生み出した人々によって定義されています。ソフトウェアコードの記述に適用されるように、complexコードを繰り返さないことです。

事実、DRYに違反すると言われるコードは、関数に繰り返しコードを抽出し、それから以前に繰り返された場所からその関数を呼び出すことによって「修正」されると言われます。コードコールsendErrorCodeは、DRY問題を解決するためのsolutionソリューションです。エラーをどうするかは、1つの決定的な場所、つまりsendErrorCode関数にあります。

@James Thorpeの答えを少し修正しますが、それは本当の批判よりもげたものです。つまり、sendErrorCodeは両方ではなく例外オブジェクトまたは文字列を受け取るべきです。

static async handleRequest(req) {
    try {
        let isAllowed = await checkIfIsAllowed(req);
        if (!isAllowed) {
            sendErrorCode(new ForbiddenException("You're not allowed to do that."));
            return;
        }
        let result = await doSomething(req); // can also raise exceptions
        sendResult(result);
    } catch(err) {
        sendErrorCode(err);
    }
}

より大きな問題は、エラーの可能性は何であり、!isAllowed例外として。例外は、異常または予測不可能な状況を処理することを目的としています。 !isAllowedは、たとえば、isAllowed質問に対する回答があるデータベースを突然クエリできないなど、その状況に固有のロジックで処理する必要がある通常の発生です。

@matchishの 提案されたソリューション は、doSomethingOnAllowedRequestのコントラクトを、例外を決してスローしないものから、例外を定期的にスローするものに変更し、すべての呼び出し元に例外処理の負担をかけます。これは、DRYの違反を引き起こす可能性があります。複数の呼び出し元に同じエラー処理コードの繰り返しを持たせることにより、抽象的には気に入らないでしょう。実際には、全体的な状況(発信者の数など)は、実際、エラーに対する同じ応答を共有します。

1
Old Pro

ジェームス・ソープの回答には、私の意見に不利な点が1つあります。 DRYではありません。どちらの場合も、sendErrorを呼び出して例外を処理します。例外をスローできるこのようなロジックを持つコード行が多数あると想像してみましょう。もっと良くなると思う。

これは私の解決策です

async function doSomethingOnAllowedRequest(req) {
    let isAllowed = await checkIfIsAllowed(req);
    if (!isAllowed) {
       throw new ForbiddenException("You're not allowed to do that.");
    }
    doSomething(req);
}
static async handleRequest(req) {
    try {
        let result = await doSomethingOnAllowedRequest(req);
        sendResult(result);
    } catch(err) {
        sendErrorCode(err);
    }
}
1
matchish