8

Express のかなり基本的な側面のように見えるものを処理する方法を理解するのに苦労しています。非同期コールバックで例外をスローするコードがある場合、その例外をキャッチする方法はありません。これは、コールバックが実行されるまでに try/catch ブロックがスコープ内にないためです。これらのシナリオでは、サーバーが応答していないことを最終的に断念するまで、ブラウザーはハングします。これは非常に悪いユーザー エクスペリエンスです。クライアントに 500 エラーをすぐに返せるようにしたいです。デフォルトのエクスプレス エラー ハンドラは、明らかにこの状況を処理しません。サンプルコードは次のとおりです。

var express = require("express");

var app = express();
app.use(app.router);
//express error handler (never called)
app.use(function(err, req, res, next) {
    console.log(err);
    res.send(500);
});

app.get("/test", function(req, res, next) {
    require("fs").readFile("/some/file", function(err, data) {
        a.b(); //blow up
    });
});

app.listen(8888);

上記のコードでは、行 ab() が「ReferenceError: a is not defined」例外をスローします。定義されたエラー ハンドラが呼び出されることはありません。この場合、ファイルが正しく読み取られたため、 fs.readFile() によって返される err オブジェクトが null であることに注意してください。バグは、非同期ハンドラー内のコードです。

ノードの uncaughtExpception の使用に関するこの投稿を読みましたが、ドキュメントにはそのメソッドを使用しないと記載されています。使用したとしても、500 応答をユーザーに返すにはどうすればよいでしょうか。エクスプレス レスポンス オブジェクトはもう使用できません。

では、このシナリオをどのように処理しますか?

4

2 に答える 2

7

OK、最初の回答には貴重な情報が含まれていますが、後から考えるとトピックから外れているため、まったく別の回答を投稿します。

簡単な答え: 正しいことは、すでに起こっていることです: プログラムはスタック トレースを出力し、エラーで終了する必要があります。

根底にある考え方

したがって、さまざまなカテゴリのエラーについて考える必要があると思います。私の最初の回答は、適切に作成されたプログラムがきれいに処理でき、処理する必要があるデータ関連のエラーを扱います。あなたが説明しているのはクラッシュです。リンク先のnode.jsドキュメントを読んだら、それは正しいです。この時点でプログラムが実行できる唯一の有用なことは、スタック トレースを使用して終了し、プロセス スーパーバイザーがプログラムを再起動して理解された状態に到達できるようにすることです。プログラムがクラッシュすると、スタックのトップに到達する例外の根本原因となる可能性のある非常に広範囲のエラーのため、基本的に回復できません。特定の例では、ソース コードのバグが修正され、アプリケーションが再デプロイされるまで、このエラーが毎回発生し続けます。テストされていないバグのあるコードがアプリケーションに侵入するのではないかと心配している場合は、

しかし、要するに、いいえ、この例外の原因となった HTTP 要求オブジェクトへの参照を取得する方法がないため、中間のリバース プロキシ レイヤーでこれを処理する以外に、ブラウザでエンド ユーザーがこれを認識する方法を変更することはできません。大雑把なタイムアウトを設定して、よりわかりやすいエラー ページを送信することもできます (もちろん、完全な HTML ドキュメント以外の要求には役に立ちません)。

ノードでのエラー処理のバイブル

私の意見では、Dave Pacheco によるNode.js でのエラー処理は、このトピックに関する決定的な作業です。それは、包括的で、広範で、徹底的です。定期的に読み返して読むことをお勧めします。


@asparagino のコメントに対処するために、未処理の例外が簡単に再現可能であるか、頻繁に発生する場合、それはエッジ ケースではなく、バグです。正しいことは、この状況に直面してもキャッチされない例外を生成しないようにコードを改善することです。実際に条件を処理することで、プログラマーのエラーを操作上のエラーに変換し、プログラムを再起動せずに、例外をキャッチせずに続行できます。

于 2013-08-26T05:08:19.680 に答える
5

経由で Express のエラー処理ミドルウェアを使用する必要がありますapp.use(error, req, res, next)。Express は、通常のミドルウェア スタックがキャッチされない例外をスローしたときに使用する別のミドルウェア スタックを維持します。(express はコールバックのアリティ (期待される引数の数) を見て、それを通常のミドルウェアまたはエラー処理ミドルウェアとして分類するだけであることに注意してください。これはエラー処理ミドルウェアです)。

そして、あなたの質問とコメントに基づいて、すべての非同期呼び出しが新しいスタックを取得するため、node.js では例外がそれほど有用ではないことを理解してください。これが、コールバックがどこでも使用され、最初の引数が普遍的にエラーになる理由です。例のtry/catchブロックは、によって直接スローされた例外のみをキャッチしますfindById(スニペットにあるようidに未定義の場合など) が、データベースへの実際の呼び出しが行われると、それだけです。呼び出しスタックは終了し、何もありません。ノードが非同期 IO コールバックを呼び出したときに、まったく異なるコール スタックが開始されるまで、さらに例外が発生する可能性があります。

答えてくれてありがとう、しかしこれは、try/catch を async コールバック内に置き、catch に next(exp) を実行させた場合にのみ機能します。非同期コールバックごとに個別の try/catch ブロックを使用することは避けたいと思います。

いいえ、そうではありません。手動で呼び出す必要はありませんnext(exp)。Express はエラーをキャッチし、エラー処理ミドルウェアをトリガーします (とにかく、開発モードで開発者にとって使いやすい例外レポート ページを Express が行う方法です)。また、非同期ライブラリは、「通常の」エラー状態であっても例外をスローしません。それらはエラーをコールバックに渡すので、一般的にノードで try/catch をそれほど気にする必要はありません。コールバック関数に渡されたエラー パラメータを決して無視しないでください。

ノードにこのボイラープレートが表示されません:

someDb.query(someCriteria, function (error, result) {
  try {
    //some code to deal with result
  } catch (exception) {
    callback(exception);
  }
});

ただし、次のように表示されます。

someDb.query(someCriteria, function (error, result) {
  if (error) {
    callback(error);
    return;
  }
  //some code to deal with result
});

ノードは IO を異なる方法で処理します。つまり、コール スタックの動作が異なり、例外の動作が異なり、エラー処理の動作が異なります。try/catch を 1 つも記述しなくても、エラーをクラッシュせずに処理する安定した node/express アプリを記述できます。(エクスプレスには、一番上までバブルするキャッチされていないエラーを処理するものがあります)。これは機能上の制限ではなく、非同期 IO の結果にすぎません。つまり、コードを別の方法で記述し、例外ではなくコールバックでエラーを処理する必要があります。それを「ありのまま」ではなく「制限」と考えると、実際には技術的な現実に過ぎないものに否定的な意味合いを与えることになります。同期パラダイムと非同期パラダイムの両方で、例外処理のためのクリーンで堅牢なパターンがあります。

于 2013-08-23T18:02:16.787 に答える