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);
}
}
}
}