asp.net mvcのエラーと例外の処理

asp.net mvc構造のエラーを処理し、例外をトラップするための受け入れられた方法は何ですか? 一般的な合意は、例外を泡立たせることです。 どのような層(ビューまたはコントローラ)で例外を処理するか(キャッチ/ユーザーフレンドリーなテキストの表示など)私はそれがコントローラで行われていることを知っていますか? EDITED: すべてのコントローラーアクションで同じエラー処理コードを繰り返さないようにしたいと思います。したがって、同じコードを繰り返さずにエラー処理を実装する方法の簡潔な例を探しています。

9
これは私がjQuery ajax内のjs側のjsonresultからのプロパティ "> stackoverflow.com/questions/8249479/…
追加された 著者 sarsnake,
傍受テクニックを使用すると、実際にここで助けになることができます。多くの依存性注入コンテナがこれをサポートしています。私は両方のasp.net mvcとの統合を持っているSpring.NETとCastle Windsorを知っています。コントローラへの呼び出しをインターセプトし、インターセプタの例外を処理します。
追加された 著者 Marijn,
この質問は良い出発点です: stackoverflow.com/questions/5442231/…
追加された 著者 Marijn,

7 答え

コントローラ内の例外を処理すると、繰り返しコードが多く発生する可能性があります。より良いアプローチは、 HandleErrorAttribute を拡張するアクションフィルタでそれらを処理することです。ここで例外を記録し、何か問題が発生したことを示す素晴らしいメッセージを表示するページにリダイレクトすることができます。

ただし、コントローラメソッドで例外を処理する必要がある場合があります。たとえば、例外から回復して適切なメッセージを表示できる場合(たとえば、ビジネスレイヤーからスローされた値あなたが提供したものは有効ではありません。その場合、特定の例外をキャッチして、ユーザーに適切なメッセージとともに同じビューを表示する必要があります。

編集:

public class CustomErrorHandlerAttribute : HandleErrorAttribute
{
     public override void OnException(ExceptionContext filterContext)
     {
         var logger = log4net.LogManager.GetLogger("SomeLoggerHere");

         logger.Error("An unhandled error occurred", filterContext.Exception);

         if (filterContext.HttpContext.Request.IsAjaxRequest())
         {
             filterContext.HttpContext.Response.Clear();
             filterContext.HttpContext.Response.Status = "500 Internal Server Error";
             filterContext.Result = new JsonResult { Data = new { ErrorMessage = filterContext.Exception.Message } };
             filterContext.ExceptionHandled = true;                
         }
         else
         {
             base.OnException(filterContext);
         }

    }

}

EDIT 2: Then you use the attribute like this:

[CustomErrorHandler]
public class AnyController : Controller
{
...
}
15
追加された
HandleErrorAttributeを使用してエラー処理を実装する方法について、明確に書かれた例を提供できますか?ありがとうございました
追加された 著者 sarsnake,
私はこのソリューションを追求したいと思いますが、上記のコードがどこに行くのかはっきりしていませんか?私のコントローラー?
追加された 著者 sarsnake,
ありがとう、それでCustomErrorHandlerAttribute自体はどこに行くのですか?同じファイルですか?それは問題ですか?
追加された 著者 sarsnake,
ありがとう、私はそれを試してみる必要があります。私は賞金を延長するつもりです。今日はそれを試す時間がないでしょう
追加された 著者 sarsnake,
私はあなたに恩恵を与えるだろう、私は同様のことをやったが、JsonResultも処理するように拡張したので、私はjsコードにカスタムエラーメッセージを返すことができます
追加された 著者 sarsnake,
この属性でコントローラクラスに注釈を付けることができます。私は答えを更新しました。
追加された 著者 uvita,
必要に応じて別のディレクトリに異なるクラスを作成できます(たとえば、フィルタ)
追加された 著者 uvita,

あなたの勘は正しいです。コントローラーである必要があります。ここに例があります:

[HttpPost]
public ActionResult Create(OrderViewModel model)
{
   if (!ModelState.IsValid)
     return View(model);

   try
   {
      repository.Save(model);
      unitOfWork.Commit();
      return RedirectToAction("Index");
   }
   catch (Exception exc)
   {
      _loggingService.Error(exc);
      ModelState.AddModelError("KeyUsedInView", exc.Message);//or, show a generic error.
   }

   return View(model);
}

メモ:

  • ModelStateを最初に確認します。有効でない場合は、戻ります。途中で取り除いてください。
  • 新しくロギングサービスを続けないでください。シングルトンインスタンスを使用し、DIを使用してコントローラに注入して、インターフェースを操作します(例: ILoggingService )。これは、ロギングサービスに他の機能を追加できることも意味します(例えば、メールサポートなど)。
  • 下位層(サービス、リポジトリなど)がエラー(カスタム、または組み込み)をスローする可能性があるため、コントローラが「アグリゲータ」であり責任を負うことが重要ですクライアントとサーバーの間のフローのために。
  • ModelState.AddModelError を使用して、ビューにエラーが表示されるようにします。ユーザーフレンドリーで、ユーザーに表示するカスタム例外を使用することもできます。下位レベルのエラー(SQLなど)の場合は、ModelStateに汎用エラーを追加するだけです(「申し訳ありませんがエラーが発生しました。後でもう一度試してください」)。
3
追加された
@ Amir978 - 私は従いません。彼はどこで質問の中でそれらのことをするように求めているのですか?
追加された 著者 RPM1984,
@ Amir978 - probsはありません。どうしてあなたが「ユーザー名を保存する」のかわからない - シナリオは何ですか?サインアップ?多分別の質問を投稿してください。エラー報告については、それがエルマのためのものです。また、Sentinelのようなものを使ってロギングエラーをキャプチャすることもできます。
追加された 著者 RPM1984,
私はむしろ各アクションで同じコードを繰り返すことはしません。
追加された 著者 sarsnake,
この場合、どのようにユーザー名を保存できますか?そして起こったすべてのエラーのレポートを作成する方法は?
追加された 著者 Amir978,
そうです。自分で解決策を見つけるように求めました。
追加された 著者 Amir978,

それは見えるほど簡単ではありません。

集中的な例外処理が必要な場合、最も速い方法は、コントローラのOnExceptionメソッドをオーバーライドすることです

[NonAction]
        protected override void OnException(ExceptionContext filterContext)
        {

            this.Session["ErrorException"] = filterContext.Exception;

            if (filterContext.Exception.GetType() == typeof(PEDException))
            {
               //Mark exception as handled
                filterContext.ExceptionHandled = true;

               //... logging, etc

               //Redirect
                filterContext.Result = this.RedirectToAction( "ShowError", "Errors");
            }

            base.OnException(filterContext);
        }

このメソッドでわかるように、すべての未処理のPEDException例外がキャッチされていますが、blからカスタム例外が発生した場合は、OnExceptionメソッドを持つベースコントローラを使用すると良い解決策になると思いますが、危険です。一般的には、他の多くの問題を避けるためにカスタム属性(ErrorAttributeFilterの拡張)を定義する方が良いと思います。たとえば、キャッシングを使用すると、アクションは実行されず、属性は常に実行されます。

詳細については、こちらをご覧ください。

2
追加された

カスタムベースコントローラを作成し、ベースコントローラクラスから継承することができます。次に、カスタムコントローラのOnExceptionをオーバーライドします。次に、あなたのコントローラのそれぞれがあなたの新しいカスタムベースコントロールラーから継承するようにしてください。

代わりに、global.asax内のApplication_Errorイベントをオーバーライドすることもできます

1
追加された

それは本当にあなたが達成しようとしているものに依存します。

すべてのエラーのカスタムメッセージを表示する単純なシナリオでは、web.configの古いカスタムカスタムエラー設定を使用できます。

これは、コントローラに到達するまでに至らないエラーでも使用されることに注意してください。 URLに正しくエンコードされた特別な値がないと問題が発生した場合と同様です。

HandleError属性または独自のカスタム属性を使用すると、他のシナリオで必要としたい細かい制御を行うことができます。

すべてのコントローラにカスタムハンドルのエラー属性を適用する場合は、グローバルアクションフィルタとして適用することで可能です。そうすれば、それを各コントローラに明示的に適用する必要はありません。

0
追加された

私は通常、Global.asaxファイル内のApplication_Errorをオーバーライドし、すべての例外について一般的な例外ページにリダイレクトしてから、いくつかの詳細を含む電子メールを送信します。十分に簡単です。ここに私が通常使っていることがあります:

protected void Application_Error(object sender, EventArgs e)
{
    if (Request.Url.ToString().StartsWith("http://localhost:"))
        return;
    string msg;
    Exception ex = Server.GetLastError().GetBaseException();
    StringBuilder sb = new StringBuilder();
    sb.AppendLine("Exception Found");
    sb.AppendLine("Timestamp: " + System.DateTime.Now.ToString());
    sb.AppendLine("Error in: " + Request.Url.ToString());
    sb.AppendLine("Browser Version: " + Request.UserAgent.ToString());
    sb.AppendLine("User IP: " + Request.UserHostAddress.ToString());
    sb.AppendLine("Error Message: " + ex.Message);
    sb.AppendLine("Stack Trace: " + ex.StackTrace);
    msg = sb.ToString();
    Server.ClearError();
    YourMailHelper.SendException("Your Site Exception", msg);
    Response.Redirect("~/Error.html");
}
0
追加された

私は、一貫性のために、例外ログ操作をBASEコントローラレベルで集中化する必要があることに同意します...私はまた、UIにユーザーフレンドリなエラーメッセージを表示する標準的な方法を、ログ要件に加えて考慮する必要があると考えています。コントローラーレベルでキャプチャされてログに記録された例外は、「Friendly Exception」でラップされ、中央の例外プロセッサーに渡されます。共有ビューフォルダ内のerror.cshtmlファイルは、発生し、コントローラレベルでログに記録され、error.cshtmlに渡された「実際の例外」をラップする「Friendly Exception」を処理して表示するのに適しています。

カスタムベースコントロールを使用して、Global.asax Application_Startイベントから呼び出されたFilterConfigクラスのRegisterGolbalFiltersメソッドを使用してHandleErrorAttributeを設定しています

Base_Controllerコード

public class Base_Controller : Controller
{
    protected override void OnException(ExceptionContext filterContext)
    {
        Exception e = filterContext.Exception;
        //Custom Exception Logging Here
        //Log Exception e
        //Elmah.Mvc.ElmahController ec = new Elmah.Mvc.ElmahController();
        base.OnException(filterContext);
    }
}

FilterConfig.csコード

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
    }
}

Global.asax Application_Start Code

void Application_Start(object sender, EventArgs e)
{
   //Code that runs on application startup
    AreaRegistration.RegisterAllAreas();
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);

}

その部分はかなり標準的です...あなたが次に行うことは、本当にユーザーに影響を与えます。 エラー処理クラスを使用して例外を一元的に処理し、例外を引き起こしたコントローラとアクションに基づいてユーザーに「フレンドリ」コンテキストベースのメッセージを表示します。以下のサンプルコードでは、catchブロックをViews/Sharedフォルダのerror.cshtmlページに移動して、シンプルにしました。以下のコードは、アプリケーションのコンテキストに基づいてフレンドリなエラーメッセージが変更される場合のサンプルコードです。メンテナンスのために例外処理をクラスに移したい場合があります。

//Check for Transport Exception with "Actual Exception" stored
//in the inner exception property
if (Model.Exception.InnerException != null)
{
    errFriendly = Model.Exception.Message;
    modelEx = Model.Exception.InnerException;
}
else
{
    modelEx = Model.Exception;
}
try
{           
    throw modelEx; 
}
catch (System.Data.SqlClient.SqlException ex)
{
    //Display Landing page friendly error for exception caused by home controller
    //Display generic data access error for all other controllers/actions
    if (Model.ActionName == "Index" && Model.ControllerName == "Home")
    {errFriendly = "Landing page cannot display product data...";}
    else
    {errFriendly = "Problem Accessing Data...";}
    errType = ex.GetType().ToString();
    errActual = ex.Message;
}

To download the entire code sample see the blog post at: http://www.prodataman.com/Blog/Post/119/MVC-Custom-Exception-Handling

0
追加された