クライアント用のAPIサービスレイヤーを開発していますが、すべてのエラーをグローバルにキャッチしてログに記録するよう要求されています。
したがって、未知のエンドポイント(またはアクション)のようなものは、ELMAHを使用するか、Global.asax
に次のようなものを追加することで簡単に処理できます。
protected void Application_Error()
{
Exception unhandledException = Server.GetLastError();
//do more stuff
}
。 。ルーティングに関連しない未処理のエラーはログに記録されません。例えば:
public class ReportController : ApiController
{
public int test()
{
var foo = Convert.ToInt32("a");//Will throw error but isn't logged!!
return foo;
}
}
また、このフィルターを登録して[HandleError]
属性をグローバルに設定しようとしました:
filters.Add(new HandleErrorAttribute());
ただし、すべてのエラーがログに記録されるわけでもありません。
上記の/test
を呼び出して生成されたようなエラーをインターセプトしてログに記録するにはどうすればよいですか?この答えは明白であるように思えますが、私はこれまで考えられるすべてを試しました。
理想的には、要求しているユーザーのIPアドレス、日付、時刻など、エラーログにいくつかの項目を追加したいです。また、エラーが発生したときにサポートスタッフに自動的に電子メールを送信できるようにしたいと考えています。これらのエラーが発生したときにインターセプトできる場合にのみ、これらすべてを実行できます。
私が答えを受け入れたダリン・ディミトロフのおかげで、私はこれを理解しました。 WebAPIはnot通常のMVCコントローラーと同じ方法でエラーを処理します。
これがうまくいったものです:
1)カスタムフィルターを名前空間に追加します。
public class ExceptionHandlingAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Exception is BusinessException)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(context.Exception.Message),
ReasonPhrase = "Exception"
});
}
//Log Critical errors
Debug.WriteLine(context.Exception);
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("An error occurred, please try again or contact the administrator."),
ReasonPhrase = "Critical Exception"
});
}
}
2)次に、フィルターをWebApiConfigクラスにグローバルに登録します。
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{action}/{id}", new { id = RouteParameter.Optional });
config.Filters.Add(new ExceptionHandlingAttribute());
}
}
または登録をスキップして、単一のコントローラーを[ExceptionHandling]
属性で装飾することができます。
Web APIがASP.NETアプリケーション内でホストされている場合、Application_Error
イベントは、表示したテストアクションの例外を含め、コード内のすべての未処理の例外に対して呼び出されます。そのため、Application_Errorイベント内でこの例外を処理するだけです。示したサンプルコードでは、HttpException
型の例外のみを処理していますが、これは明らかにConvert.ToInt32("a")
コードの場合ではありません。したがって、必ずすべての例外をログに記録して処理してください。
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; // TODO: don't forget that here you have many other status codes to test // and handle in addition to 401. } else { // It was not an HttpException. This will be executed for your test action. // Here you should log and handle this case. Use the unhandledException instance here } } }
Web APIの例外処理は、さまざまなレベルで実行できます。 detailed article
は、さまざまな可能性を説明しています。
グローバル例外フィルターとして登録できるカスタム例外フィルター属性
[AttributeUsage(AttributeTargets.All)]
public class ExceptionHandlingAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Exception is BusinessException)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(context.Exception.Message),
ReasonPhrase = "Exception"
});
}
//Log Critical errors
Debug.WriteLine(context.Exception);
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("An error occurred, please try again or contact the administrator."),
ReasonPhrase = "Critical Exception"
});
}
}
カスタムアクション呼び出し
public class MyApiControllerActionInvoker : ApiControllerActionInvoker
{
public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken)
{
var result = base.InvokeActionAsync(actionContext, cancellationToken);
if (result.Exception != null && result.Exception.GetBaseException() != null)
{
var baseException = result.Exception.GetBaseException();
if (baseException is BusinessException)
{
return Task.Run<HttpResponseMessage>(() => new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(baseException.Message),
ReasonPhrase = "Error"
});
}
else
{
//Log critical error
Debug.WriteLine(baseException);
return Task.Run<HttpResponseMessage>(() => new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(baseException.Message),
ReasonPhrase = "Critical Error"
});
}
}
return result;
}
}
以前の回答への追加として。
昨日、ASP.NET Web API 2.1は正式に リリース済み でした。
例外をグローバルに処理する別の機会を提供します。
詳細は sample に記載されています。
簡単に言えば、グローバル例外ロガーおよび/またはグローバル例外ハンドラー(1つのみ)を追加します。
構成に追加します。
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
// There can be multiple exception loggers.
// (By default, no exception loggers are registered.)
config.Services.Add(typeof(IExceptionLogger), new ElmahExceptionLogger());
// There must be exactly one exception handler.
// (There is a default one that may be replaced.)
config.Services.Replace(typeof(IExceptionHandler), new GenericTextExceptionHandler());
}
そしてその実現:
public class ElmahExceptionLogger : ExceptionLogger
{
public override void Log(ExceptionLoggerContext context)
{
...
}
}
public class GenericTextExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
context.Result = new InternalServerErrorTextPlainResult(
"An unhandled exception occurred; check the log for more information.",
Encoding.UTF8,
context.Request);
}
}
なぜ再スローなど?これは機能し、サービスはステータス500などを返します。
public class LogExceptionFilter : ExceptionFilterAttribute
{
private static readonly ILog log = LogManager.GetLogger(typeof (LogExceptionFilter));
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
log.Error("Unhandeled Exception", actionExecutedContext.Exception);
base.OnException(actionExecutedContext);
}
}
ハンドルエラーアクションフィルターのようなことをすることを考えましたか
[HandleError]
public class BaseController : Controller {...}
また、[HandleError]
のカスタムバージョンを作成して、エラー情報やその他すべての詳細をログに書き込むこともできます。
すべてをtry/catchでラップし、未処理の例外をログに記録してから、渡します。より良い組み込みの方法がない限り。
ここにリファレンスがあります キャッチまたは未処理の例外をすべてキャッチ
(編集:oh API)