web-dev-qa-db-ja.com

コントローラーでの例外処理(ASP.NET MVC)

コントローラーのアクションから呼び出される独自のコードによって例外がスローされた場合、どのように処理する必要がありますか? try-catchステートメントがまったくないベストプラクティスの例がたくさんあります。たとえば、リポジトリのデータにアクセスする場合:

public ViewResult Index()
{
    IList<CustomModel> customModels = _customModelRepository.GetAll();
    return View(customModels);
}

呼び出しがアクセスできないデータベースに対する呼び出しであり、たとえばEntity FrameworkのようなORMを使用している場合、明らかにこのコードは例外をスローする可能性があります。

しかし、私が見ることができるのは、例外が発生してユーザーに厄介なエラーメッセージを表示することだけです。

私はHandleError属性を知っていますが、未処理の例外が発生した場合にエラーページにリダイレクトするために主に使用されることを理解しています。

もちろん、このコードはtry-catchでラップすることもできますが、特にロジックが多い場合はうまく分離できません。

public ViewResult Index()
{
    if (ValidationCheck())
    {
        IList<CustomModel> customModels = new List<CustomModel>();
        try
        {
            customModels = _customModelRepository.GetAll();
        }
        catch (SqlException ex)
        {
            // Handle exception
        }

        if (CustomModelsAreValid(customModels))
            // Do something
        else
            // Do something else
    }

    return View();
}

以前、データベース呼び出しなどの例外をスローする可能性のあるすべてのコードを抽出して、エラーを処理し、ユーザーにメッセージを表示するためにメッセージを返すDataProviderクラスに入れました。

これを処理する最善の方法は何だろうと思っていましたか?一部の例外はそうすべきではないので、私はいつもエラーページに戻りたくありません。代わりに、ユーザーへのエラーメッセージが通常のビューで表示されます。以前の方法は正しかったのですか、それともより良い解決策はありますか?

21
Serberuss

よりユーザーフレンドリーなメッセージを表示するには、3つのことを行います。

  1. グローバル例外ハンドラーを活用してください。 MVCの場合:Global.asaxのApplication_Error。ここでの使用方法を学ぶ: http://msdn.Microsoft.com/en-us/library/24395wz3(v = vs.100).aspx
  2. 例外をUserFriendlyExceptionにサブクラス化します。基礎となるすべてのサービスクラスで、従来の例外ではなくこのUserFriendlyExceptionをスローするように最善を尽くしています。これらのカスタム例外には、常にユーザーに意味のあるメッセージを入れようとします。その主な目的は、Application_Errorメソッドで例外の型チェックを実行できるようにすることです。 UserFriendlyExceptionsには、「Hey!91度は有効な緯度値ではありません!」など、サービスの詳細に設定したユーザーフレンドリーなメッセージを使用します。定期的な例外の場合は、未処理の場合があるため、「エラーが発生しました。修正するために最善を尽くします!」など、より一般的なエラーメッセージを表示します。
  3. また、ユーザーフレンドリービューまたはJSONのレンダリングを担当するErrorControllerを作成します。これは、Application_Errorメソッドからメソッドが呼び出されるコントローラーです。

EDIT:ASP.NET Web APIは密接に関連しているため、言及したいと思いました。 Web APIエンドポイントのコンシューマーは必ずしもブラウザーであるとは限らないため、エラーの処理方法は少し異なります。まだ「FriendlyException」(上記の#2)を使用していますが、ErrorControllerにリダイレクトする代わりに、すべてのエンドポイントがErrorプロパティを含むある種のベースタイプを返すようにします。したがって、例外がWeb APIコントローラーに至るまでバブルする場合は、API応答のErrorプロパティにそのエラーを必ず貼り付けます。このエラーメッセージは、APIコントローラーが依存するクラスからバブルアップしたフレンドリメッセージであるか、例外タイプがFriendlyExceptionでない場合は一般的なメッセージです。これにより、消費側クライアントは、API応答のErrorプロパティが空かどうかを簡単に確認できます。エラーが存在する場合はメッセージを表示し、存在しない場合は通常どおり続行します。ナイスなのは、わかりやすいメッセージの概念のため、メッセージは一般的な「エラー」よりもユーザーにとってはるかに意味があるかもしれないということです。メッセージ。 Xamarinを使用してモバイルアプリを作成するときにこの戦略を使用します。WebサービスとiOS/Androidアプリ間でC#タイプを共有できます。

23
NovaJoe

Asp.Net MVCでは、コントローラーのOnExceptionメソッドをオーバーライドすることもできます。

protected override void OnException(ExceptionContext filterContext)
{
    if (filterContext.ExceptionHandled)
    {
        return;
    }
    filterContext.Result = new ViewResult
    {
        ViewName = ...
    };
    filterContext.ExceptionHandled = true;
}

これにより、必要に応じて例外を参照するメッセージを含むカスタムエラーページにリダイレクトできます。

18

エラーを処理するコントローラーを持つプロジェクトを参照するプロジェクトがいくつかあるため、OnExceptionオーバーライドを使用しました。

Security/HandleErrorsController.cs

protected override void OnException(ExceptionContext filterContext) 
{
    MyLogger.Error(filterContext.Exception);   //method for log in EventViewer

    if (filterContext.ExceptionHandled)
        return;

    filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;

    filterContext.Result = new JsonResult
    {
        Data = new
        {
            Success = false, 
            Error = "Please report to admin.",
            ErrorText = filterContext.Exception.Message,
            Stack = filterContext.Exception.StackTrace
        },
        JsonRequestBehavior = JsonRequestBehavior.AllowGet
    };
    filterContext.ExceptionHandled = true;
}
1

エラー処理には非常に多くの方法があるため、答えは常に「依存する」ため、このような質問はすべて建設的ではありません。

例外は基本的に回復不能であるため、多くの人がHandleErrorメソッドを使用することを好みます。つまり、オブジェクトを返せない場合はどうしますか?とにかくエラーを表示しますよね?

問題は、エラーをどのように表示するかです。エラーページを表示することが許容される場合、HandleErrorは正常に機能し、エラーを記録する簡単な場所を提供します。 Ajaxを使用している場合、またはより洗練されたものが必要な場合は、その方法を開発する必要があります。

DataProviderクラスについて話します。これが基本的にリポジトリです。リポジトリにビルドしてみませんか?

0