złapać wszystkie nieobsłużone wyjątki w ASP.NET Web Api

113

Jak złapać wszystko nieobsłużone wyjątki występujące w ASP.NET Web Api, aby móc je rejestrować?

Do tej pory próbowałem:

  • Utwórz i zarejestruj ExceptionHandlingAttribute
  • Zaimplementuj Application_Errormetodę w programieGlobal.asax.cs
  • Subskrybuj AppDomain.CurrentDomain.UnhandledException
  • Subskrybuj TaskScheduler.UnobservedTaskException

ExceptionHandlingAttributePowodzeniem obsługuje wyjątków, które są generowane wewnątrz metod działania kontrolera i filtrów działania, ale inne wyjątki nie są obsługiwane, na przykład:

  • Wyjątki zgłaszane, gdy wykonanie IQueryablezwrócone przez metodę akcji nie powiedzie się
  • Wyjątki zgłoszone przez program obsługi komunikatów (tj. HttpConfiguration.MessageHandlers)
  • Wyjątki zgłaszane podczas tworzenia instancji kontrolera

Zasadniczo, jeśli wyjątek spowoduje zwrócenie do klienta wewnętrznego błędu serwera 500, chcę, aby był on rejestrowany. Wdrożenie Application_Errorwykonało tę pracę dobrze w formularzach sieci Web i MVC - czego mogę używać w interfejsie Web Api?

Joe Daley
źródło
Czy próbowałeś korzystać z monitorowania kondycji ASP.NET ? Po prostu włącz ją i zobacz, czy Twoje wyjątki nie są rejestrowane w dzienniku zdarzeń.
John Saunders,
Monitorowanie kondycji przechwytuje moje wyjątki potoku MVC, ale nie moje wyjątki potoku interfejsu API sieci Web.
Joe Daley,
Dzięki - zajęło mi trochę czasu,
zanim

Odpowiedzi:

156

Jest to teraz możliwe dzięki WebAPI 2.1 (zobacz Co nowego ):

Utwórz jedną lub więcej implementacji IExceptionLogger. Na przykład:

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

Następnie zarejestruj się w HttpConfiguration aplikacji, wewnątrz wywołania zwrotnego konfiguracji, takiego jak:

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

lub bezpośrednio:

GlobalConfiguration.Configuration.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());
dekady
źródło
7
@NeilBarnwell Tak, interfejs API sieci Web 2.1 odpowiada wersji zestawu System.Web.Http 5.1.0 . Potrzebujesz więc tej wersji lub nowszej, aby skorzystać z rozwiązania opisanego tutaj. Zobacz wersje pakietu
NuGet
2
Niektóre błędy 500 nadal nie są przez to przechwytywane, np. HttpException - host zdalny zamknął połączenie. Czy jest jeszcze miejsce dla global.asax Application_Error do obsługi błędów poza przetwarzaniem internetowego interfejsu API?
Avner
11
Uwielbiam to, jak szczegółowe jest oficjalne dokumentacja w msdn, a 99% programistów naprawdę chce, aby tylko 8 linii kodu rejestrowało błędy.
Rocklan
20

Odpowiedź Yuval dotyczy dostosowywania odpowiedzi na nieobsłużone wyjątki przechwycone przez interfejs API sieci Web, a nie do rejestrowania, jak wspomniano na połączonej stronie . Szczegółowe informacje można znaleźć w sekcji Kiedy używać na tej stronie. Rejestrator jest zawsze wywoływany, ale program obsługi jest wywoływany tylko wtedy, gdy można wysłać odpowiedź. Krótko mówiąc, użyj programu rejestrującego do logowania i modułu obsługi, aby dostosować odpowiedź.

Nawiasem mówiąc, używam zestawu v5.2.3, a ExceptionHandlerklasa nie ma HandleCoremetody. Myślę, że odpowiednik jest Handle. Jednak zwykłe tworzenie podklas ExceptionHandler(jak w odpowiedzi Yuvala) nie działa. W moim przypadku muszę zaimplementować IExceptionHandlerw następujący sposób.

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

Zauważ, że w przeciwieństwie do loggera, rejestrujesz program obsługi, zastępując domyślny program obsługi, a nie dodając.

config.Services.Replace(typeof(IExceptionHandler),
    new OopsExceptionHandler(config.Services.GetExceptionHandler()));
Duoc Tran
źródło
1
To świetne rozwiązanie, powinno to być akceptowane rozwiązanie umożliwiające „wyłapywanie lub rejestrowanie wszystkich błędów”. Nigdy nie mogłem się dowiedzieć, dlaczego to nie zadziałało, kiedy właśnie rozszerzałem ExceptionHandler.
Rajiv
Świetne rozwiązanie. Po załadowaniu potoku MVC dla żądania działa to świetnie. Usługi IIS nadal obsługują wyjątki do tego czasu, w tym podczas uruchamiania OWIN w pliku startup.cs. Jednak w pewnym momencie po tym, jak rozkręcenie kończy przetwarzanie startup.cs, wykonuje swoją pracę cudownie.
Gustyn
18

Odpowiadając na moje własne pytanie, to niemożliwe!

Obsługa wszystkich wyjątków, które powodują wewnętrzne błędy serwera, wydaje się być podstawową funkcją, którą powinien mieć interfejs API sieci Web, więc wysłałem do firmy Microsoft żądanie do obsługi błędów globalnych dla interfejsu API sieci Web :

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

Jeśli się zgadzasz, przejdź do tego linku i zagłosuj na to!

W międzyczasie doskonały artykuł dotyczący obsługi wyjątków interfejsu API sieci Web ASP.NET przedstawia kilka różnych sposobów przechwytywania kilku różnych kategorii błędów. Jest to bardziej skomplikowane niż powinno i nie obejmuje wszystkiego wyłapuje wewnętrznych błędów serwera, ale jest to najlepsze dostępne obecnie podejście.

Aktualizacja: Globalna obsługa błędów jest teraz zaimplementowana i dostępna w nocnych kompilacjach! Zostanie wydany w ASP.NET MVC v5.1. Oto jak to będzie działać: https://aspnetwebstack.codeplex.com/wikipage?title=Global%20Error%20Handling

Joe Daley
źródło
wydaje się, że jest to powód, aby używać kontrolera do wywołań ajax zamiast interfejsu API sieci Web .. granice są już zamazane .. chociaż jeśli ELMAH jest w stanie to uchwycić, może jest sposób
Sonic Soul
4
Globalna obsługa błędów została dodana w interfejsie Web API 2.1. Zobacz moją odpowiedź, aby uzyskać więcej informacji.
decyduje
10

Możesz również utworzyć globalną procedurę obsługi wyjątków, implementując IExceptionHandlerinterfejs (lub dziedzicząc ExceptionHandlerklasę bazową). Będzie to ostatnia wywołana w łańcuchu wykonania, po wszystkich zarejestrowanych IExceptionLogger:

IExceptionHandler obsługuje wszystkie nieobsłużone wyjątki ze wszystkich kontrolerów. To ostatnia pozycja na liście. Jeśli wystąpi wyjątek, IExceptionLogger zostanie wywołany jako pierwszy, następnie kontroler ExceptionFilters i, jeśli nadal nie jest obsługiwany, implementacja 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);
        }
    }
}

Więcej na ten temat tutaj .

Yuval Itzchakov
źródło
Ciekawe, gdzie jest to zarejestrowane?
ThunD3eR
-1

Możesz mieć istniejące bloki try-catch, o których nie jesteś świadomy.

Myślałem, że moja nowa global.asax.Application_Errormetoda nie jest konsekwentnie wywoływana dla nieobsłużonych wyjątków w naszym starszym kodzie.

Następnie znalazłem kilka bloków try-catch w środku stosu wywołań o nazwie Response.Write on the Exception. To było to. Porzucił tekst na ekranie, a następnie zabił kamień wyjątkowy martwy.

Tak więc wyjątki były obsługiwane, ale obsługa nie przyniosła żadnego pożytku. Po usunięciu tych bloków try-catch wyjątki propagowane do metody Application_Error zgodnie z oczekiwaniami.

Ratunek
źródło