Подтвердить что ты не робот

Unhandled Exception Global Handler для OWIN/Katana?

Каков надлежащий способ реализации глобального обработчика захвата исключений в реализации Katana (OWIN)?

В самообслуживаемой реализации OWIN/Katana, выполняемой как Azure Cloud Service (рабочая роль), я поместил этот код в Middleware:

throw new Exception("pooo");

Затем я поместил этот код в метод конфигурации класса запуска, установив точку останова в обработчике событий:

 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

Я также попытался переместить проводку и обработчик в метод Onerart рабочей роли, но все же точка останова не попала.

Я вообще не использую WebAPI, но смотрел сообщения о том, что там сделано, но я не нашел ничего понятного, так что я здесь.

Работает на .NET Framework 4.5.2, VS 2013.

Все идеи оценены. Спасибо.

4b9b3361

Ответ 1

Попробуйте написать собственное промежуточное ПО и поместить его в качестве первого промежуточного программного обеспечения:

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

Поместите его в качестве первого промежуточного программного обеспечения:

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

Ответ 2

Попробуйте это:

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

@Khanh ответить отлично; Хороший краткий код и хорошо объясненный.

Это не работало для меня. Я добавил тестовое исключение для одного из моих контроллеров .NET MVC, например:

throw new Exception("Test GlobalExceptionMiddleware");

Исключение не было обнаружено в методе Invoke.

Это очень легко отлаживать:

  • Поместите контрольную точку в тестовое исключение.
  • Нажмите F10 или перейдите по каждой строке, пока не получите код, обрабатывающий исключение.

Для меня был добавлен код в BaseController overriding OnException. Этот код обрабатывал исключение, а не восстанавливал. Надеюсь, это полезное дополнение к @Khanh, чтобы ответить.

Update

ОК, я понимаю, что этот вопрос отмечен веб-API, и мой вариант использования - это .NET MVC, но я нашел этот вопрос при поиске промежуточного программного обеспечения Owin. Когда я отлаживаю свое решение с помощью тестового исключения и смотрю $exception в представлении "Локали", исключение исчезает, когда я возвращаюсь к промежуточному программному обеспечению Owin. Поэтому я не использовал код в @Khanh, чтобы ответить. Вместо этого я добавил блоки 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;
}

Этот ответ поймает любые исключения при запуске приложения и любом исключении в контроллере. Этого достаточно для моих нужд.

Ответ 4

Вы можете использовать настраиваемые атрибуты фильтра исключений.

public static class WebApiConfiguration
{
    public static void Register(HttpConfiguration config)
    {
        config.RegisterExceptionFilters();
    }
}

public static class HttpConfigurationFilterExtensions
{
    public static HttpConfiguration RegisterExceptionFilters(this HttpConfiguration httpConfig)
    {
        httpConfig.Filters.Add(new CustomExceptionFilterAttribute());

        return httpConfig;
    }
}

public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
    public override void OnException(HttpActionExecutedContext context)
    {
        if (context.Exception is CustomException)
        {
            context.Response = new HttpResponseMessage((HttpStatusCode)CustomHttpStatusCode.CustomCode);
        }
    }
}

public class CustomException : Exception
{
    public CustomException(string message)
        : base(message)
    {
    }
}