LoginSignup
48
44

More than 1 year has passed since last update.

ASP.NET MVCの集約例外処理

Last updated at Posted at 2014-02-05

ASP.NET MVCでの集約例外処理の実装例です。
ログの出力処理とAjaxリクエスト処理時の振る舞いの変更をします。

HandleErrorAttribute.OnException

Controller内で起きた例外を処理する集約例外ハンドラを実装します。

Ajaxリクエストの場合はデフォルトの例外処理は何もせず、
ステータスコードを500、応答本文を例外情報を含んだJSONにして、
$.ajax().fail()で例外処理をしやすくしておきます。

なおLogUtil.LogControllerError()というメソッドは別途定義されているものとします。

public class GlobalHandleErrorAttribute: HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }

        /// 例外発生時は常にログを取っておく
        LogUtil.LogControllerError(filterContext);

        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            /// Application_Errorは呼ばれない
            HandleAjaxRequestException(filterContext);
        }
        else
        {
            /// custom errorが有効でなければ
            /// base.OnException()でExceptionHandledがtrueにならないので
            /// Application_Errorも呼ばれる
            base.OnException(filterContext);
        }
    }

    private void HandleAjaxRequestException(ExceptionContext filterContext)
    {
        if (filterContext.ExceptionHandled)
        {
            return;
        }

        filterContext.Result = new JsonResult
        {
            Data = new {
                Message = filterContext.Exception.ToString(),
            },
            JsonRequestBehavior = JsonRequestBehavior.AllowGet
        };
        filterContext.ExceptionHandled = true;
        filterContext.HttpContext.Response.Clear();
        filterContext.HttpContext.Response.StatusCode =
            (int)HttpStatusCode.InternalServerError;
        filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
    }
}

FilterConfig.csのRegisterGlobalFilters()を編集します。
もともとあるfilters.Add(new HandleErrorAttribute());をコメントアウトして、
代わりにGlobalHandleErrorAttributeを登録します。

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    //filters.Add(new HandleErrorAttribute());
    filters.Add(new GlobalHandleErrorAttribute());
}

Application_Error

Controller外で起きた例外を補足するにはApplication_Error()を定義します。

なおLogUtil.LogError()LogUtil.LogErrorSimple()というメソッドは別途定義されているものとします。

protected void Application_Error(object sender, EventArgs e)
{
    if (Server != null)
    {
        var ex = Server.GetLastError();
        if (ex != null)
        {
            if (ex is HttpException &&
                ((HttpException)ex).GetHttpCode() == (int)HttpStatusCode.NotFound)
            {
                /// NotFoundを相手にするとログが大変になるので無視
                return;
            }

            /// CustomErrorが無効な場合は
            /// Controller内でおきた例外が二重にログ出力されてしまうことに注意。
            /// CustomErrorが有効な場合は
            /// Controller外でおきた例外のみここでログ出力される。
            try
            {
                LogUtil.LogError(ex, HttpContext.Current);
            }
            catch (Exception)
            {
                LogUtil.LogErrorSimple(ex);
            }
        }
    }
}
48
44
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
48
44