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


113

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

これまでのところ、私は試しました:

  • を作成して登録する ExceptionHandlingAttribute
  • Application_Errorメソッドを実装するGlobal.asax.cs
  • 申し込む AppDomain.CurrentDomain.UnhandledException
  • 申し込む TaskScheduler.UnobservedTaskException

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

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

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


ASP.NETヘルスモニタリングを使用してみましたか?有効にして、例外がイベントログに記録されていないかどうかを確認してください。
John Saunders

ヘルスモニタリングは、MVCパイプラインの例外をキャッチしますが、Web APIパイプラインの例外はキャッチしません。
Joe Daley、

ありがとう-コンストラクタ/依存性注入の問題をログに記録できなかった理由を理解するのにしばらく時間がかかりました
。WebAPIの

回答:


156

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

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

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

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

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

または直接:

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

7
@NeilBarnwellはい、Web API 2.1はSystem.Web.Httpアセンブリバージョン5.1.0に対応しています。したがって、ここで説明するソリューションを使用するには、このバージョン以上が必要です。nugetパッケージのバージョンを
2014年

2
特定の500エラーは、これでも捕捉されません。HttpException-リモートホストが接続を閉じました。global.asax Application_ErrorがWeb API処理外のエラーを処理する場所はまだありますか?
Avner

11
msdnの公式docoがどれほど詳細であるか、そして99%の開発者が本当に望んでいるのは、エラーをログに記録するための8行のコードだけです。
ロックラン

20

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

ちなみに、私はアセンブリv5.2.3を使用しており、ExceptionHandlerクラスにはHandleCoreメソッドがありません。同等のものはだと思いますHandle。ただし、ExceptionHandler(Yuvalの回答のように)単純なサブクラス化は機能しません。私の場合、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()));

1
これは優れたソリューションであり、「すべてのエラーをキャッチまたはログに記録する」ための受け入れられたソリューションになるはずです。ExceptionHandlerを拡張しただけでは、なぜ機能しないのかわかりませんでした。
Rajiv 2017年

素晴らしいソリューション。MVCパイプラインがリクエスト用にロードされると、これはうまく機能します。IISは、startup.csでOWINを起動するときを含め、それまでの例外を処理します。ただし、スピンアップがstartup.csの処理を完了した後のある時点で、それは見事に機能します。
Gustyn 2017年

18

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

内部サーバーエラーの原因となるすべての例外を処理することは、Web APIが持つべき基本的な機能のように思われるため、Web APIのグローバルエラーハンドラーをMicrosoftにリクエストしました。

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

同意する場合は、そのリンクにアクセスして投票してください。

それまでの間、優れた記事であるASP.NET Web APIの例外処理は、いくつかの異なるカテゴリのエラーをキャッチするためのいくつかの異なる方法を示しています。これは必要以上に複雑であり、すべての内部サーバーエラーをキャッチするわけではありませんが、現在利用できる最善のアプローチです。

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


Web APIの代わりにajax呼び出しにコントローラーを使用する理由のように思われます。境界はすでにぼやけています。ただし、ELMAHがそれをキャプチャできる場合は、方法がある可能性があります
Sonic Soul

4
グローバルエラー処理がWeb API 2.1に追加されました。詳細については、私の回答を参照してください。
2014年

10

また、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);
        }
    }
}

詳細はこちら


これはどこに登録されているのですか?
ThunD3eR

-1

気づいていない既存のtry-catchブロックがある可能性があります。

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

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

したがって、例外は処理されていましたが、処理は何の役にも立ちませんでした。これらのtry-catchを削除すると、期待どおりにApplication_Errorメソッドに伝播される例外がブロックされます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.