webdevqa.jp.net

ASP.NET MVCで、私の見解では未処理の例外を表示するのに最適なものは何ですか?

Web.configに次のものがあります。

 <customErrors mode="On" defaultRedirect="Error">
  <error statusCode="404" redirect="Error/NotFound" />
</customErrors>

私は

 [HandleError]

私のHomeControllerクラスの一番上にあります。テストするために、単に例外をスローするアクションを作成して実行します。 。そしてそれは私のにリダイレクトします

 ErrorController/Index

メソッドですが、HandleErrorInfoにバインドするビューに到達すると、モデルがnullになるため、エラーへの参照が失われました。

リダイレクトでエラーが失われることに関係していると確信しているので、何かが足りないかどうか、スタックトレースとエラーメッセージを表示するビューを表示できる提案があるかどうかを確認したいと思いました。

18
leora

誤解が見えます。コントローラーアクションに対してMVCredirectを実行します。

ただし、defaultRedirect自体は_Web Form_規則であるため、制限されます。別のコントローラーにリダイレクトすると、HttpContextが失われ、それによってHandleErrorInfoオブジェクトが失われます。

_[HandleError]_属性には、エラーメッセージを送信するためのViewが必要です。上記の例では、ErrorController用の_Views/Error_フォルダーがあり、その中にIndexビューがあると仮定します。フィルタコンテキストでHandleErrorInfoオブジェクトをそのビューに送信する場合は、

この構文を試してください:

_[HandleError(View="~/Views/Error/Index")]
Public class HomeController : Controller
_

しかし、ロギングはどうですか?!?!?

あなたの意図は、単にエラースタックをユーザーに表示するだけではなく、moreだと思います。実際、あなたにはそのような意図はまったくないと思います。私はあなたの本当の目的があなたのエラーを(おそらくdbに)記録し、あなたのユーザーにいくつかの当たり障りのないメッセージを表示することであると思います。

これまで説明してきたのは、「私のビューで未処理の例外を表示するのに最適な方法」でした。 _[HandleError]_属性はそのために適しています。

ただし、次のステップ(エラーのログ記録)に移動する場合は、いくつかのオプションがあります。

1) ベースコントローラーのOn Exceptionメソッドをオーバーライドします ; MVCコントローラークラスから継承する独自のControllerを作成しますが、OnExceptionメソッドをオーバーライドします。このアプローチは、[HandleError]属性と組み合わせて使用​​できます。

2) カスタム例外ハンドラーを作成します エラーをログに記録する独自の例外ハンドラーを作成します。例外ハンドラーは、選択したビューを呼び出すか、[HandleError(order=2)]と連携して機能できます。これは、フィルター属性が優先順位を適用する順序引数を取ることができるためです。


Nitin Sawantは、エラービューがどのようになるかを尋ねます。ザ・

_@model System.Web.Mvc.HandleErrorInfo
<h2>Exception details</h2>
<p> Controller: @Model.ControllerName </p>
<p> Action: @Model.ActionName </p>
<p> Exception: @Model.Exception </p>
_
24
Dave Alperovich

すべてのエラー(HandleError属性を持つコントローラーで発生するエラーだけでなく)を処理するmaxlegoと同様のことを行います。

私のMvcApplicationクラス(global.asax.cs内)には次のものがあります。

public class MvcApplication : HttpApplication
{
    // usual stuff here...

    protected void Application_Error(object sender, EventArgs e)
    {
        Server.HandleError(((MvcApplication)sender).Context);
    }
}

上記のコードは、便利なもののMVCライブラリからの拡張メソッドを使用しています。これが適切に配置されていれば、エラー処理属性、customErrors構成、またはカスタムフィルターは必要ありません。代わりに、拡張メソッドはエラーの詳細をログに記録してから、次のいずれかの適切なビューを呼び出します。

  • アクセスが拒否されました
  • 見つかりません
  • 内部サーバーエラー

これを機能させるための拡張メソッドコードは次のとおりです。

public static class HttpServerUtilityExtensions
{
    private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

    public static void HandleError(this HttpServerUtility server, HttpContext httpContext)
    {
        var currentController = " ";
        var currentAction = " ";
        var currentRouteData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));

        if (currentRouteData != null)
        {
            if (currentRouteData.Values["controller"] != null && !String.IsNullOrEmpty(currentRouteData.Values["controller"].ToString()))
                currentController = currentRouteData.Values["controller"].ToString();

            if (currentRouteData.Values["action"] != null && !String.IsNullOrEmpty(currentRouteData.Values["action"].ToString()))
                currentAction = currentRouteData.Values["action"].ToString();
        }

        var exception = server.GetLastError();
        Logger.ErrorException(exception.Message, exception);

        var controller = DependencyResolver.Current.GetService<ErrorController>();
        var routeData = new RouteData();
        var action = "InternalServerError";

        if (exception is HttpException)
        {
            var httpEx = exception as HttpException;

            switch (httpEx.GetHttpCode())
            {
                case 404:
                    action = "NotFound";
                    break;

                case 401:
                    action = "AccessDenied";
                    break;
            }
        }

        httpContext.ClearError();
        httpContext.Response.Clear();
        httpContext.Response.StatusCode = exception is HttpException ? ((HttpException)exception).GetHttpCode() : 500;
        httpContext.Response.TrySkipIisCustomErrors = true;

        routeData.Values["controller"] = "Error";
        routeData.Values["action"] = action;

        controller.ViewData.Model = new HandleErrorInfo(exception, currentController, currentAction);
        ((IController)controller).Execute(new RequestContext(new HttpContextWrapper(httpContext), routeData));
    }
}

上記はエラーの詳細をログに記録するためにNLogを使用していますが、他の何かをサポートするために簡単に変更できることに注意してください。また、このメソッドは、ErrorControllerを解決するときにIoCコンテナを尊重します。

6
MarkG

この小さなコードを使用して、ユーザーが処理したエラーページを表示しました。ページが見つからなかったか、その他のエラーが発生したかどうか。

    void Application_Error(object sender, EventArgs e)
    {
        // this value can be fetched from config or depend on DEBUG smybol
        if (!handleErrors)
            return;

        var error = Server.GetLastError();
        var code = (error is HttpException) ? (error as HttpException).GetHttpCode() : 500;

        if (code == 404)
        {
            // do something if page was not found. log for instance
        }
        else
        {
            // collect request info and log exception
        }

        // pass exception to ErrorsController
        Request.RequestContext.RouteData.Values["ex"] = error;

        // execute controller action
        IController errorController = new ErrorsController();
        errorController.Execute(new RequestContext(new HttpContextWrapper(Context), Request.RequestContext.RouteData));
    }

そして、エラーコントローラは次のようになります。詳細な例外が必要な場合は、RouteDataからアクセスできます

public class ErrorsController : Controller
{
    /// <summary>
    /// Page not found
    /// </summary>
    /// <returns></returns>
    public ActionResult Http404()
    {
        return View();
    }

    /// <summary>
    /// All other errors
    /// </summary>
    /// <param name="actionName"></param>
    protected override void HandleUnknownAction(string actionName)
    {
        // in case detailed exception is required.
        var ex = (Exception) RouteData.Values["ex"];
        return View();
    }
}

Httpコードごとに異なるビューを追加できます。アクションHttp {Code}を実装するだけです

2
maxlego