Fangen Sie alle nicht behandelten Ausnahmen in ASP.NET Web Api ab

113

Wie fange ich alle nicht behandelten Ausnahmen ab, die in ASP.NET Web Api auftreten, damit ich sie protokollieren kann?

Bisher habe ich versucht:

  • Erstellen und registrieren Sie eine ExceptionHandlingAttribute
  • Implementieren Sie eine Application_ErrorMethode inGlobal.asax.cs
  • Etwas abonnieren AppDomain.CurrentDomain.UnhandledException
  • Etwas abonnieren TaskScheduler.UnobservedTaskException

Der behandelt ExceptionHandlingAttributeerfolgreich Ausnahmen, die in Controller-Aktionsmethoden und Aktionsfiltern ausgelöst werden, andere Ausnahmen werden jedoch nicht behandelt, z. B.:

  • Ausnahmen, die ausgelöst werden, wenn eine IQueryablevon einer Aktionsmethode zurückgegebene Methode nicht ausgeführt werden kann
  • Ausnahmen, die von einem Nachrichtenhandler ausgelöst werden (dh HttpConfiguration.MessageHandlers)
  • Ausnahmen beim Erstellen einer Controller-Instanz

Wenn eine Ausnahme dazu führen soll, dass ein 500 Internal Server Error an den Client zurückgegeben wird, möchte ich, dass er protokolliert wird. Die Implementierung Application_Errorhat diesen Job in Web Forms und MVC gut gemacht - was kann ich in Web Api verwenden?

Joe Daley
quelle
Haben Sie versucht, ASP.NET Health Monitoring zu verwenden ? Aktivieren Sie es einfach und prüfen Sie, ob Ihre Ausnahmen nicht im Ereignisprotokoll protokolliert sind.
John Saunders
Die Zustandsüberwachung erfasst meine MVC-Pipeline-Ausnahmen, jedoch nicht meine Web-Api-Pipeline-Ausnahmen.
Joe Daley
Danke - Ich habe eine Weile
gebraucht,

Antworten:

156

Dies ist jetzt möglich mit WebAPI 2.1 (siehe Was ist neu ):

Erstellen Sie eine oder mehrere Implementierungen von IExceptionLogger. Beispielsweise:

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

Registrieren Sie sich dann bei der HttpConfiguration Ihrer Anwendung in einem Konfigurationsrückruf wie folgt:

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

oder direkt:

GlobalConfiguration.Configuration.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());
entscheidet
quelle
7
@NeilBarnwell Ja, Web API 2.1 entspricht der System.Web.Http-Assembly-Version 5.1.0 . Sie benötigen also diese Version oder höher, um die hier beschriebene Lösung zu verwenden. Siehe die Nuget-Paketversionen
entscheidet am
2
Bestimmte 500 Fehler werden davon immer noch nicht erfasst, z. HttpException - Der Remote-Host hat die Verbindung geschlossen. Gibt es noch einen Platz für global.asax Application_Error, um Fehler außerhalb der Web-API-Verarbeitung zu behandeln?
Avner
11
Ich finde es toll, wie detailliert das offizielle Dokument auf msdn ist und was 99% der Entwickler wirklich wollen, sind nur die 8 Codezeilen, um Fehler zu protokollieren.
Rocklan
20

Die Antwort des Yuval dient zum Anpassen von Antworten auf nicht behandelte Ausnahmen, die von der Web-API abgefangen wurden, und nicht zum Protokollieren, wie auf der verlinkten Seite angegeben . Weitere Informationen finden Sie im Abschnitt Verwendungszweck auf der Seite. Der Logger wird immer aufgerufen, der Handler jedoch nur, wenn eine Antwort gesendet werden kann. Kurz gesagt, verwenden Sie den Logger zum Protokollieren und den Handler zum Anpassen der Antwort.

Übrigens verwende ich Assembly v5.2.3 und die ExceptionHandlerKlasse hat die HandleCoreMethode nicht. Das Äquivalent, denke ich, ist Handle. Eine einfache Unterklasse ExceptionHandler(wie in Yuvals Antwort) funktioniert jedoch nicht. In meinem Fall muss ich IExceptionHandlerFolgendes implementieren .

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

Beachten Sie, dass Sie Ihren Handler im Gegensatz zum Logger registrieren, indem Sie den Standardhandler ersetzen und nicht hinzufügen.

config.Services.Replace(typeof(IExceptionHandler),
    new OopsExceptionHandler(config.Services.GetExceptionHandler()));
Duoc Tran
quelle
1
Dies ist eine großartige Lösung. Dies sollte die akzeptierte Lösung sein, um alle Fehler abzufangen oder zu protokollieren. Ich hätte nie herausfinden können, warum es bei mir nicht funktioniert hat, als ich gerade ExceptionHandler erweitert habe.
Rajiv
Tolle Lösung. Sobald die MVC-Pipeline für eine Anforderung geladen wurde, funktioniert dies hervorragend. IIS behandelt die Ausnahmen bis dahin weiterhin, auch beim Hochfahren von OWIN in startup.cs. Irgendwann, nachdem das Hochfahren die Verarbeitung von startup.cs abgeschlossen hat, macht es seine Arbeit wunderbar.
Gustyn
18

Um meine eigene Frage zu beantworten, ist dies nicht möglich!

Die Behandlung aller Ausnahmen, die interne Serverfehler verursachen, scheint eine grundlegende Funktion zu sein, über die die Web-API verfügen sollte. Daher habe ich bei Microsoft eine Anfrage für einen globalen Fehlerbehandler für die Web-API gestellt :

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

Wenn Sie einverstanden sind, gehen Sie zu diesem Link und stimmen Sie dafür ab!

In der Zwischenzeit zeigt der hervorragende Artikel ASP.NET Web API Exception Handling einige verschiedene Möglichkeiten, um einige verschiedene Fehlerkategorien abzufangen. Es ist komplizierter als es sein sollte und fängt nicht alle internen Serverfehler ab, aber es ist der beste Ansatz, der heute verfügbar ist.

Update: Die globale Fehlerbehandlung ist jetzt implementiert und in den nächtlichen Builds verfügbar! Es wird in ASP.NET MVC v5.1 veröffentlicht. So funktioniert es: https://aspnetwebstack.codeplex.com/wikipage?title=Global%20Error%20Handling

Joe Daley
quelle
scheint ein Grund zu sein, Controller für Ajax-Aufrufe anstelle von Web-API zu verwenden. Die Grenzen sind bereits verschwommen. Wenn ELMAH es jedoch erfassen kann, gibt es vielleicht einen Weg
Sonic Soul
4
Die globale Fehlerbehandlung wurde jetzt in der Web-API 2.1 hinzugefügt. Siehe meine Antwort für weitere Details.
entscheidet
10

Sie können auch einen globalen Ausnahmebehandler erstellen, indem Sie die IExceptionHandlerSchnittstelle implementieren (oder die ExceptionHandlerBasisklasse erben ). Es wird das letzte sein, das in der Ausführungskette aufgerufen wird, nachdem alle registriert wurden IExceptionLogger:

Der IExceptionHandler behandelt alle nicht behandelten Ausnahmen von allen Controllern. Dies ist der letzte in der Liste. Wenn eine Ausnahme auftritt, wird zuerst der IExceptionLogger aufgerufen, dann der Controller ExceptionFilters und, falls noch nicht behandelt, die IExceptionHandler-Implementierung.

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

Mehr dazu hier .

Yuval Itzchakov
quelle
Nur neugierig, wo ist das registriert?
Donnerstag,
-1

Möglicherweise sind Try-Catch-Blöcke vorhanden, die Ihnen nicht bekannt sind.

Ich dachte, meine neue global.asax.Application_ErrorMethode würde nicht konsequent für nicht behandelte Ausnahmen in unserem Legacy-Code aufgerufen.

Dann fand ich in der Mitte des Aufrufstapels einige Try-Catch-Blöcke mit dem Namen Response.Write on the Exception-Text. Das war's. Wirf den Text auf den Bildschirm und tötete die Ausnahme steinertot.

Die Ausnahmen wurden also behandelt, aber die Behandlung war nicht nützlich. Nachdem ich diese Try-Catch-Blöcke entfernt hatte, wurden die Ausnahmen erwartungsgemäß an die Application_Error-Methode weitergegeben.

Ressource
quelle