web-dev-qa-db-ja.com

むやみにキャッチ例外(ポケモン例外処理)を受け入れることはいつまでも受け入れられますか?

通常、私は例外を予期していません。例外が発生しても、コードで修正できないものです-ユーザーの入力不良またはデータベースとの接続の問題。

しかし、エラーが発生するので、少なくとも、何かがうまくいかなかったことをユーザーに通知し、エラーをログに記録したいと思います。

私の例外処理は一般的に次のようになります:

public JsonResult Create(UserCreateEditViewModel model)
{
    try
    {
        if (!ModelState.IsValid) return Json(new { IsOk = false, Message = "Some of your data was invalid, please try again." });

        var User = new User
        {
            // map UserCreateEditViewModel properties to User properties
        };

        OrtundEntities Db = new OrtundEntities();
        Db.Users.Add(User);
        Db.SaveChanges();

        return Json
    }
    catch(Exception ex)
    {
        // can't correct user data or repair connection
        // issues to the database so just report the error to the user
        return ReportError(ex, "Create User");
    }
}

public JsonResult ReportError(Exception ex, string action)
{
    return Json(new { IsOk = false, Title = action, Message = ex.Message });
}

ほとんどの場合、 ポケモン例外処理 は推奨されませんが、エラーで実際に私ができることは何もないため、 ポケモン例外処理 単純に使用することはできませんエラーを報告するには?

14
Ortund

はい。例外がエンドユーザーまたは外部システムに公開される可能性がある場合は常に、考えられるすべての例外をキャッチし、一般的な処理(ロギングなど)を実行して、一般的なメッセージを出力することをお勧めします。ユーザビリティの理由とセキュリティの理由の両方(例外には機密情報が含まれる場合があります)。

しかし、多くの場合、このトップレベルの例外ハンドラーは実際のtry..catchブロックとして実装されず、アプリケーションフレームワークによって処理されます。一般的なWebフレームワークでは、すべてのリクエストハンドラやページジェネレータの周囲にtry...catchを記述する必要はありませんが、汎用の例外処理コードを1か所に登録します。 ASP.NET MVCを使用する場合は、OnExceptionまたは同様のものを使用して、多くの定型例外処理を節約できます。

重要なことは、アプリケーションまたは要求がまだ終了しているであることです。推奨されないのは、例外を無差別にキャッチし、何も起こらなかったかのように実行を継続することです。予期しない例外は、アプリケーションの状態が何らかの未知の方法で侵害されていることを意味するため、実行を継続すると、エラーやデータの破損が発生し、デバッグが非常に困難になる可能性があります。

すべてをキャッチすることが理にかなっている別の(あまり一般的ではない)状況:重要でないサービスを提供する信頼できないライブラリがある場合。たとえば、サードパーティが動的に読み込まれるモジュールを提供できるプラグインアーキテクチャのアプリケーションがあるとします。障害のあるプラグインでアプリケーション全体を停止させたくないので、これらのプラグインを呼び出すときにすべての例外をキャッチすることは理にかなっています。

内部モジュールでさえ、信頼できないものとして扱われる可能性があります。たとえば、マイクロソフトがOfficeのサブシステムをこのように扱うという噂を聞いたことがあります。文法チェッカーモジュールが予期しない例外をスローする場合、アプリケーション全体を終了させるのではなく、「文法チェッカーに予期しないエラーが発生しました」というダイアログを表示してチェックなしで続行することをお勧めします。

これは、モジュールがメインアプリケーションから明確に分離されているである場合にのみ機能するため、モジュール内のエラーによってプログラム全体の状態が破損しないことを確認できます。たとえば、プラグインまたはモジュールは、破損した状態であるため、最初の予期しない例外の後で無効にする必要があります。したがって、これは、問題のモジュールをこのレベルで分離できるようにアプリケーションが明示的に設計されている場合にのみ可能です。

11
JacquesB

ポケモンの例外... Ever許容できる?

(N)everとは言わないでください。例外の簡潔な概念化は、その理由を示しています。


tryブロックは、処理しないことを選択した状況を示します

どこでtrycatch、および事後処理の例外を処理するかは設計上の問題です。

フォーカスされたtryブロックは、プログラムの懸念を伝えます。信頼性の低い接続、未定義のユーザーデータの組み合わせ、行き止まりの特定のロジックパスなど。 DBやファイルシステムコールのように制御できないリソースをラップすることになっていることは誰もが知っていますが、それは設計と統合する必要があり、それと一貫している必要があります。

確かに、DB接続が失敗した場合、私たちは多くのことを行うことはできませんが、例外をキャッチ、スロー、再スロー、またはその両方を行うことは、絶対に必要な選択ではありません。さらに、「回復」したくないビジネスドメインの状況が存在する場合があります。

できるだけ情報を提供してください

何が原因でどのような状態/データがそれを引き起こしたのかわからないので、例外のデバッグに多くの時間を費やしているのは悲しいことに面白いです。アプリケーションを爆発させるだけでも、本質的には同じです。

スローされた場所が正確にわかっている場合、または例外のタイプがわかっている場合は、関連する詳細を取得して渡すことができます。たとえば、SelectCommandについて話している場合、SqlExceptionで使用されるパラメーター値。

例外のタイプ自体は、アプリケーションのビジネスドメインを表すことができます。アプリケーション固有の行き止まりを成文化、強化、表現する1つ以上のアプリケーション固有の例外を作成する場合があります。これらのクラスには、特定のユーザーデータを確実にキャプチャするためのコンストラクターがある場合があります。


ポケモンは無意味です

上記のコードからtry/catchを削除すると、動作はexactlyと同じになります。 I.E.すべてを試すことは、何もしないことと同じです。

「Create User」メッセージが追加されたことは、スタックトレースを通じて発信ポイントがわかっているので不必要です。そして、メッセージは例外を引き起こした特定の状況に対してゼロのコンテキストを追加します。

狭いtryスコープ==コンテキスト

DB呼び出しのみをラップしている場合は、どの例外が予期されるかが正確にわかります。また、Exception.Dataプロパティに格納する関連データを知っています。

狭いcatchスコープ==コンテキスト

ボブの「逆キャッチ句コンテキストルール」は、私が作成した古い格言です。 tryのスコープが広いほど(正当な理由があると想定しています)、catchにしたいより具体的な例外の種類があります。

3
radarbob

Exceptionをキャッチすることは通常、間違った行為ですが、許容できる場合と正しい行為がある場合があります。

Web APIを作成するときは、例外についての情報をクライアントにあまり公開したくない場合があります。 APIにエラーインジケーターを設計して、クライアントがこれを使用して次に何をするかを決定できるようにすることもできます。このような状況では、APIの境界ですべての例外をキャッチすることが適切な場合があります。確かに、例外をスローする場所の近くで例外をキャッチする必要がありますが、サードパーティのライブラリではスローできる例外を完全に文書化/公開していないため、APIを作成するときは注意する必要があります。 APIの境界は、処理するリクエストにアクセスできるように処理を設計できるため、エラーをログに記録するのに最適な場所にもなります。失敗したリクエストと例外ログを組み合わせて作業することは、失敗の原因を特定する非常に効果的な方法です。

さて、私のコメントで述べたように、特定のケースを見ると、例外メッセージをクライアントに返すのは間違っているようです。ほとんどのユーザーは例外メッセージを解釈することができなくなり、せいぜい、エラーレポートで何を言っているかを報告できるようになります。仲介者を切り取り、代わりにエラーをログに記録します。代わりに、何らかの一般的なエラーをユーザーに返します。

また、Createを呼び出すものはすべて、IsOk==falseを受け取ったときに何をすべきかを知っていることを確認してください。そうしないと、特に指示がない限り、呼び出しは失敗ではなく成功として扱われます。

1
forsvarir

はい。少なくとも、非技術的なユーザーにサービスを提供するアプリケーションについては、ユーザーに意味のある指示を与えている場合、クライアント側で正当化できます。

たとえば、「内部エラーが発生しました。ページを再読み込みしてください。」

例外は、問題を修正するために意味のある何かを実行できる時点でのみ、常にキャッチする必要があります。

1
svidgen

番号

他の人は、ユーザーにすべての例外をニースダイアログに表示したり、ログに保存したりすることを望んでいると述べています。これらはひどく悪い考えではありません。

しかし、例外がStackOverflowExceptionの場合はどうなりますか? OutOfMemoryExceptionClrException

本質的に、あなたは単にそれらをすべてできないことができない。試してみると、ある程度の価値は得られるかもしれませんが、期待よりも少なくなります。例外自体がなくても、十分にログに記録するだけで十分です。

1
Magus