web-dev-qa-db-ja.com

ASP.NET MVCでのエラー処理

ASP.NET MVCのコントローラーからスローされた例外を正しく処理するにはどうすればよいですか? HandleError属性は、MVCインフラストラクチャによってスローされた例外のみを処理し、自分のコードによってスローされた例外は処理しないようです。

このweb.configを使用する

<customErrors mode="On">
    <error statusCode="401" redirect="/Errors/Http401" />
</customErrors>

次のコードで

namespace MvcApplication1.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            // Force a 401 exception for testing
            throw new HttpException(401, "Unauthorized");
        }
    }
}

私が望んでいたものにはなりません。代わりに、web.configを変更して実際のエラー情報を確認するよう指示する一般的なASP.NETエラーページが表示されます。ただし、例外をスローする代わりに無効なビューを返すと、/ Shared/Views/Error.aspxページが表示されます。

return View("DoesNotExist");

上記のようにコントローラー内で例外をスローすると、すべてのHandleError機能がバイパスされるようです。エラーページを作成する正しい方法は何ですか。また、MVCインフラストラクチャでNiceをプレイするにはどうすればよいですか。

64
Adrian Anttila

Kazimanzurrashaidのおかげで、Global.asax.csで次のようになりました。

protected void Application_Error()
{
    Exception unhandledException = Server.GetLastError();
    HttpException httpException = unhandledException as HttpException;
    if (httpException == null)
    {
        Exception innerException = unhandledException.InnerException;
        httpException = innerException as HttpException;
    }

    if (httpException != null)
    {
        int httpCode = httpException.GetHttpCode();
        switch (httpCode)
        {
            case (int) HttpStatusCode.Unauthorized:
                Response.Redirect("/Http/Error401");
                break;
        }
    }
}

サポートする必要がある追加のHTTPエラーコードに基づいて、HttpContollerにさらにページを追加できます。

21
Adrian Anttila

Controller.OnException(ExceptionContext context)。それをオーバーライドします。

protected override void OnException(ExceptionContext filterContext)
{
    // Bail if we can't do anything; app will crash.
    if (filterContext == null)
        return;
        // since we're handling this, log to elmah

    var ex = filterContext.Exception ?? new Exception("No further information exists.");
    LogException(ex);

    filterContext.ExceptionHandled = true;
    var data = new ErrorPresentation
        {
            ErrorMessage = HttpUtility.HtmlEncode(ex.Message),
            TheException = ex,
            ShowMessage = !(filterContext.Exception == null),
            ShowLink = false
        };
    filterContext.Result = View("ErrorPage", data);
}
63
Will

HandleError属性は、MVCインフラストラクチャによってスローされた例外のみを処理し、自分のコードによってスローされた例外は処理しないようです。

それはただ間違っています。実際、HandleErrorは、独自のコードまたは独自のコードによって呼び出されたコードでスローされた例外のみを「処理」します。言い換えれば、アクションが呼び出しスタックにある例外のみです。

表示されている動作の実際の説明は、スローしている特定の例外です。 HandleErrorの動作は、HttpExceptionでは異なります。ソースコードから:

        // If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method),
        // ignore it.
        if (new HttpException(null, exception).GetHttpCode() != 500) {
            return;
        }
13
Craig Stuntz

HandleError属性を持つHttpCodeに基づいて特定のErrorPageを表示できるとは思わないので、この目的のためにHttpModuleを使用したいと思います。特定のエラーごとに異なるページが存在するフォルダー「ErrorPages」があり、マッピングが通常のWebフォームアプリケーションと同じweb.configで指定されていると仮定します。また、次はエラーページを表示するために使用されるコードです。

public class ErrorHandler : BaseHttpModule{

public override void OnError(HttpContextBase context)
{
    Exception e = context.Server.GetLastError().GetBaseException();
    HttpException httpException = e as HttpException;
    int statusCode = (int) HttpStatusCode.InternalServerError;

    // Skip Page Not Found and Service not unavailable from logging
    if (httpException != null)
    {
        statusCode = httpException.GetHttpCode();

        if ((statusCode != (int) HttpStatusCode.NotFound) && (statusCode != (int) HttpStatusCode.ServiceUnavailable))
        {
            Log.Exception(e);
        }
    }

    string redirectUrl = null;

    if (context.IsCustomErrorEnabled)
    {
        CustomErrorsSection section = IoC.Resolve<IConfigurationManager>().GetSection<CustomErrorsSection>("system.web/customErrors");

        if (section != null)
        {
            redirectUrl = section.DefaultRedirect;

            if (httpException != null)
            {
                if (section.Errors.Count > 0)
                {
                    CustomError item = section.Errors[statusCode.ToString(Constants.CurrentCulture)];

                    if (item != null)
                    {
                        redirectUrl = item.Redirect;
                    }
                }
            }
        }
    }

    context.Response.Clear();
    context.Response.StatusCode = statusCode;
    context.Response.TrySkipIisCustomErrors = true;

    context.ClearError();

    if (!string.IsNullOrEmpty(redirectUrl))
    {
        context.Server.Transfer(redirectUrl);
    }
}

}

6

これを読んでいる他の人が経験している可能性のあるもう1つの可能性(あなたの場合は真実ではありません)は、エラーページがエラーを投げているか、実装していないことです:

 System.Web.Mvc.ViewPage<System.Web.Mvc.HandleErrorInfo>

この場合、デフォルトのエラーページが表示されます(そうしないと、カスタムエラーページに自分自身を送信しようとするため、無限ループが発生します)。これはすぐには分かりませんでした。

このモデルは、エラーページに送信されるモデルです。エラーページでサイトの他の部分と同じマスターページを使用し、他のモデル情報が必要な場合は、独自の[HandleError]タイプの属性を作成するか、OnExceptionなどをオーバーライドする必要があります。

3
Simon_Weaver

Controller.OnException()アプローチを選択しました。これは私にとって論理的な選択です。ASP.NETMVCを選択したため、フレームワークレベルにとどまり、可能な場合は基礎となるメカニズムをいじることを避けます。

次の問題に遭遇しました。

ビュー内で例外が発生すると、そのビューからの部分的な出力がエラーメッセージとともに画面に表示されます。

FilterContext.Resultを設定する前に、応答をクリアすることでこれを修正しました-このように:

        filterContext.HttpContext.Response.Clear(); // gets rid of any garbage
        filterContext.Result = View("ErrorPage", data);

これが誰か他の人の時間を節約することを願っています:-)

2
mindplay.dk
     protected override void OnException (ExceptionContext filterContext )
    {
        if (filterContext != null && filterContext.Exception != null)
        {
            filterContext.ExceptionHandled = true;
            this.View("Error").ViewData["Exception"] = filterContext.Exception.Message;
            this.View("Error").ExecuteResult(this.ControllerContext);
        }
    }
2
bharat

Jeff Atwoodの ユーザーフレンドリーな例外処理モジュール はMVCに最適です。 MVCプロジェクトのソースコードをまったく変更せずに、web.configで完全に構成できます。ただし、200ステータスではなく元のHTTPステータスを返すには、わずかな変更が必要です。これに関連する フォーラム投稿 を参照してください。

基本的に、Handler.vbでは、次のようなものを追加できます。

' In the header...
Private _exHttpEx As HttpException = Nothing

' At the top of Public Sub HandleException(ByVal ex As Exception)...
HttpContext.Current.Response.StatusCode = 500
If TypeOf ex Is HttpException Then
    _exHttpEx = CType(ex, HttpException)
    HttpContext.Current.Response.StatusCode = _exHttpEx.GetHttpCode()
End If
1
Martin_ATS