web-dev-qa-db-ja.com

ASP.NET Web Apiで未処理の例外をすべてキャッチする

ASP.NET Web Apiで発生する未処理の例外をallキャッチしてログに記録するにはどうすればよいですか?

これまで私は試しました:

  • ExceptionHandlingAttribute を作成して登録します
  • Application_ErrorGlobal.asax.csメソッドを実装します
  • AppDomain.CurrentDomain.UnhandledExceptionを購読する
  • TaskScheduler.UnobservedTaskExceptionを購読する

ExceptionHandlingAttributeは、コントローラーアクションメソッドおよびアクションフィルター内でスローされた例外を正常に処理しますが、他の例外は処理されません。例:

  • アクションメソッドによって返されたIQueryableの実行に失敗した場合にスローされる例外
  • メッセージハンドラーによってスローされた例外(つまり、HttpConfiguration.MessageHandlers
  • コントローラーインスタンスの作成時にスローされる例外

基本的に、例外によって500内部サーバーエラーがクライアントに返される場合、ログに記録する必要があります。 Application_Errorを実装すると、この仕事はWebフォームとMVCでうまくいきました。WebApiで何が使えますか?

111
Joe Daley

これはWebAPI 2.1で可能になりました( 新機能 を参照):

IExceptionLoggerの1つ以上の実装を作成します。例えば:

public class TraceExceptionLogger : ExceptionLogger
{
    public override void Log(ExceptionLoggerContext context)
    {
        Trace.TraceError(context.ExceptionContext.Exception.ToString());
    }
}

次に、次のような構成コールバック内で、アプリケーションのHttpConfigurationに登録します。

config.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());

または直接:

GlobalConfiguration.Configuration.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());
154
decates

Yuvalの答えは、リンクされた page に記載されているように、ロギングではなく、Web APIによってキャッチされた未処理の例外に対する応答をカスタマイズすることです。詳細については、ページの「いつ使用するか」セクションを参照してください。ロガーは常に呼び出されますが、ハンドラーは応答を送信できる場合にのみ呼び出されます。要するに、 logger を使用してログを記録し、ハンドラーを使用して応答をカスタマイズします。

ところで、私はAssembly v5.2.3を使用しており、 ExceptionHandler クラスにはHandleCoreメソッドがありません。相当するのは、Handleです。ただし、(Yuvalの答えのように)ExceptionHandlerをサブクラス化するだけでは機能しません。私の場合、次のようにIExceptionHandlerを実装する必要があります。

internal class OopsExceptionHandler : IExceptionHandler
{
    private readonly IExceptionHandler _innerHandler;

    public OopsExceptionHandler (IExceptionHandler innerHandler)
    {
        if (innerHandler == null)
            throw new ArgumentNullException(nameof(innerHandler));

        _innerHandler = innerHandler;
    }

    public IExceptionHandler InnerHandler
    {
        get { return _innerHandler; }
    }

    public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
    {
        Handle(context);

        return Task.FromResult<object>(null);
    }

    public void Handle(ExceptionHandlerContext context)
    {
        // Create your own custom result here...
        // In dev, you might want to null out the result
        // to display the YSOD.
        // context.Result = null;
        context.Result = new InternalServerErrorResult(context.Request);
    }
}

ロガーとは異なり、追加するのではなく、デフォルトのハンドラを置き換えることでハンドラを登録することに注意してください。

config.Services.Replace(typeof(IExceptionHandler),
    new OopsExceptionHandler(config.Services.GetExceptionHandler()));
19
Duoc Tran

私自身の質問に答えるために、これは不可能です!

内部サーバーエラーの原因となるすべての例外の処理は、Web APIが持つべき基本的な機能のように思えるので、MicrosoftにWeb APIのグローバルエラーハンドラー

https://aspnetwebstack.codeplex.com/workitem/1001

同意したら、そのリンクにアクセスして投票してください!

それまでの間、優れた記事 ASP.NET Web API例外処理 は、いくつかの異なるカテゴリのエラーをキャッチするいくつかの異なる方法を示しています。それは本来よりも複雑であり、allインターナルサーバーエラーをキャッチしませんが、今日利用可能な最良のアプローチです。

更新:グローバルエラー処理が実装され、ナイトリービルドで利用可能になりました! ASP.NET MVC v5.1でリリースされます。仕組みは次のとおりです。 https://aspnetwebstack.codeplex.com/wikipage?title=Global%20Error%20Handling

18
Joe Daley

IExceptionHandlerインターフェイスを実装する(またはExceptionHandler基本クラスを継承する)ことにより、グローバル例外ハンドラーを作成することもできます。すべての登録済みIExceptionLoggerの後、実行チェーンで最後に呼び出されます。

IExceptionHandlerは、すべてのコントローラーからのすべての未処理の例外を処理します。これはリストの最後です。例外が発生した場合、IExceptionLoggerが最初に呼び出され、次にコントローラーExceptionFiltersが呼び出され、まだ処理されない場合はIExceptionHandler実装が呼び出されます。

public class OopsExceptionHandler : ExceptionHandler
{
    public override void HandleCore(ExceptionHandlerContext context)
    {
        context.Result = new TextPlainErrorResult
        {
            Request = context.ExceptionContext.Request,
            Content = "Oops! Sorry! Something went wrong."        
        };
    }

    private class TextPlainErrorResult : IHttpActionResult
    {
        public HttpRequestMessage Request { get; set; }

        public string Content { get; set; }

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            HttpResponseMessage response = 
                             new HttpResponseMessage(HttpStatusCode.InternalServerError);
            response.Content = new StringContent(Content);
            response.RequestMessage = Request;
            return Task.FromResult(response);
        }
    }
}

詳細は こちら です。

9
Yuval Itzchakov

私の新しいglobal.asax.Application_Errorメソッドは、レガシーコードの未処理の例外に対して一貫して呼び出されていないと思っていました。

次に、コールスタックの途中で、ExceptionテキストでResponse.Writeを呼び出したtry-catchブロックをいくつか見つけました。それでした。画面上のテキストをダンプし、例外の石を殺しました。

0
Resource