web-dev-qa-db-ja.com

OWIN / Katanaの未処理の例外グローバルハンドラー?

Katana(OWIN)実装でグローバルな例外キャッチャーハンドラーを実装する適切な方法は何ですか?

Azureクラウドサービス(ワーカーロール)として実行される自己ホスト型のOWIN/Katana実装では、このコードをミドルウェアに配置しました。

throw new Exception("pooo");

次に、このコードをStartupクラスのConfigurationメソッドに配置し、イベントハンドラーにブレークポイントを設定しました。

 AppDomain.CurrentDomain.UnhandledException += 
    CurrentDomain_UnhandledExceptionEventHandler;

同じクラスのイベントハンドラー(最初の行にブレークポイントを設定):

private static void CurrentDomain_UnhandledExceptionEventHandler(object sender, UnhandledExceptionEventArgs e)
{
    var exception = (Exception)e.ExceptionObject;
    Trace.WriteLine(exception.Message);
    Trace.WriteLine(exception.StackTrace);
    Trace.WriteLine(exception.InnerException.Message);
}

コードを実行すると、ブレークポイントにヒットしません。ただし、Visual Studioの出力ウィンドウにはこれが含まれます。

A first chance exception of type 'System.Exception' occurred in redacted.dll
A first chance exception of type 'System.Exception' occurred in mscorlib.dll

ワイヤーアップとハンドラーをWorker Role OnStartメソッドに移動しようとしましたが、それでもブレークポイントにヒットしません。

私はWebAPIをまったく使用していませんが、そこで行われていることに関する投稿を見ましたが、明確なものは何も見つかりませんでしたので、ここにいます。

.NET Framework 4.5.2、VS 2013で実行します。

すべてのアイデアに感謝します。ありがとう。

34
Snowy

カスタムミドルウェアを作成し、それをfirstミドルウェアとして配置してみてください。

public class GlobalExceptionMiddleware : OwinMiddleware
{
   public GlobalExceptionMiddleware(OwinMiddleware next) : base(next)
   {}

   public override async Task Invoke(IOwinContext context)
   {
      try
      {
          await Next.Invoke(context);
      }
      catch(Exception ex)
      {
          // your handling logic
      }
   }
 }

firstミドルウェアとして配置します。

public class Startup
{
    public void Configuration(IAppBuilder builder)
    {
        var config = new HttpConfiguration();

        builder.Use<GlobalExceptionMiddleware>();
        //register other middlewares
    }
}

このミドルウェアを最初のミドルウェアとして登録すると、他のミドルウェアで発生した例外(スタックトレースを下る)が上に伝播し、try/catchこのミドルウェアのブロック。

常に最初のミドルウェアとして登録することは必須ではありません。一部のミドルウェアでグローバルな例外処理が必要ない場合は、これらのミドルウェアをこのミドルウェアの前に登録するだけです。

public class Startup
{
    public void Configuration(IAppBuilder builder)
    {
        var config = new HttpConfiguration();

        //register middlewares that don't need global exception handling. 
        builder.Use<GlobalExceptionMiddleware>();
        //register other middlewares
    }
}
40
Khanh TO

これを試して:

public class CustomExceptionHandler : IExceptionHandler
{    
    public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
    {
      // Perform some form of logging

      context.Result = new ResponseMessageResult(new HttpResponseMessage
      {
        Content = new StringContent("An unexpected error occurred"),
        StatusCode = HttpStatusCode.InternalServerError
      });

      return Task.FromResult(0);
    }
}

起動時:

public void Configuration(IAppBuilder app)
{
  var config = new HttpConfiguration();

  config.Services.Replace(typeof(IExceptionHandler), new CustomExceptionHandler());
}
3
Greg R Taylor

@Khanh TOの答えは素晴らしいです。すてきな簡潔なコード、そしてよく説明されています。

それは私のために働いていませんでした。次のように、.NET MVCコントローラーの1つにテスト例外を追加しました。

throw new Exception("Test GlobalExceptionMiddleware");

例外はInvokeメソッドでキャッチされませんでした。

これは非常に簡単にデバッグできます。

  1. テスト例外にブレークポイントを置きます。
  2. ヒット F10 または、is例外を処理するコードに到達するまで各行をステップオーバーします。

私にとっては、BaseControllerをオーバーライドするOnExceptionに追加されたコードがありました。このコードは例外を処理し、再スローではありませんでした。これが@Khanh TOの答えに対する有益な補遺になることを願っています。

更新

OK、この質問はWeb APIでタグ付けされており、私のユースケースは.NET MVCですが、この質問はOwinミドルウェアの検索で見つかりました。テスト例外を使用してソリューションをデバッグし、$exception Localsビューでは、Owinミドルウェアに戻ったときの例外はgoneです。したがって、@ Khanh TOの回答にあるコードは使用していません。代わりに、try/catchブロックをStartupおよびGlobal.asaxに追加しました。私のベースコントローラーには次のようなOnExceptionもあります:

protected override void OnException(ExceptionContext filterContext)
{
    if (filterContext.ExceptionHandled)
    {
        return;
    }
    var e = filterContext.Exception;
    Log.Error("BaseController.OnException caught exception:", e);
    filterContext.ExceptionHandled = true;
}

この回答は、アプリケーションの起動時の例外とコントローラーの例外をキャッチします。これで私のニーズには十分です。

0
Jess