intercetta tutte le eccezioni non gestite in ASP.NET Web Api


113

Come faccio a rilevare tutte le eccezioni non gestite che si verificano in ASP.NET Web Api in modo da poterle registrare?

Finora ho provato:

  • Crea e registra un file ExceptionHandlingAttribute
  • Implementa un Application_Errormetodo inGlobal.asax.cs
  • Iscriviti a AppDomain.CurrentDomain.UnhandledException
  • Iscriviti a TaskScheduler.UnobservedTaskException

La ExceptionHandlingAttributegestisce con successo le eccezioni che vengono gettati all'interno dei metodi di azione di controllo e filtri d'azione, ma altre eccezioni non sono gestiti, per esempio:

  • Eccezioni generate quando un IQueryablerestituito da un metodo di azione non viene eseguito
  • Eccezioni generate da un gestore di messaggi (cioè HttpConfiguration.MessageHandlers)
  • Eccezioni generate durante la creazione di un'istanza del controller

Fondamentalmente, se un'eccezione sta per causare la restituzione di un errore interno del server 500 al client, voglio che venga registrato. L'implementazione ha Application_Errorsvolto bene questo lavoro in Web Forms e MVC: cosa posso usare in Web Api?


Hai provato a utilizzare ASP.NET Health Monitoring ? Abilitalo e verifica se le tue eccezioni non vengono registrate nel registro eventi.
John Saunders

Il monitoraggio dell'integrità rileva le eccezioni della pipeline MVC, ma non le eccezioni della pipeline API Web.
Joe Daley

Grazie - Mi ci è voluto un po 'per capire perché non riuscivo a registrare i miei problemi di
inserimento nel

Risposte:


156

Questo è ora possibile con WebAPI 2.1 (vedi Novità ):

Crea una o più implementazioni di IExceptionLogger. Per esempio:

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

Quindi registrati con HttpConfiguration della tua applicazione, all'interno di un callback di configurazione in questo modo:

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

o direttamente:

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

7
@NeilBarnwell Sì, l'API Web 2.1 corrisponde all'assembly System.Web.Http versione 5.1.0 . Quindi è necessaria questa versione o una versione successiva per utilizzare la soluzione descritta qui. Vedi le versioni del pacchetto nuget
decade il

2
Alcuni errori 500 ancora non vengono rilevati da questo, ad es. HttpException: l'host remoto ha chiuso la connessione. C'è ancora un posto per global.asax Application_Error per gestire gli errori al di fuori dell'elaborazione web api?
Avner

11
Mi piace quanto sia dettagliato il doco ufficiale su msdn e ciò che il 99% degli sviluppatori vuole veramente sono solo le 8 righe di codice per registrare gli errori.
Rocklan

20

La risposta di Yuval è per personalizzare le risposte alle eccezioni non gestite rilevate dall'API Web, non per la registrazione, come indicato nella pagina collegata . Fare riferimento alla sezione Quando utilizzarlo nella pagina per i dettagli. Il logger viene sempre chiamato ma il gestore viene chiamato solo quando è possibile inviare una risposta. In breve, usa il logger per accedere e il gestore per personalizzare la risposta.

A proposito, sto usando l'assembly v5.2.3 e la ExceptionHandlerclasse non ha il HandleCoremetodo. L'equivalente, penso, è Handle. Tuttavia, la semplice sottoclasse ExceptionHandler(come nella risposta di Yuval) non funziona. Nel mio caso, devo implementare IExceptionHandlerquanto segue.

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

Nota che, a differenza del logger, registri il tuo gestore sostituendo il gestore predefinito, non aggiungendo.

config.Services.Replace(typeof(IExceptionHandler),
    new OopsExceptionHandler(config.Services.GetExceptionHandler()));

1
Questa è un'ottima soluzione, questa dovrebbe essere la soluzione accettata per "rilevare o registrare tutti gli errori". Non avrei mai potuto capire perché non funzionasse per me quando stavo semplicemente estendendo ExceptionHandler.
Rajiv

Ottima soluzione. Una volta che la pipeline MVC è stata caricata per una richiesta, funziona alla grande. IIS gestisce ancora le eccezioni fino a quel momento, incluso quando avvia OWIN in startup.cs. Tuttavia, a un certo punto, dopo che lo spin up termina l'elaborazione di startup.cs, fa il suo lavoro meravigliosamente.
Gustyn

18

Per rispondere alla mia domanda, questo non è possibile!

La gestione di tutte le eccezioni che causano errori interni al server sembra una funzionalità di base che l'API Web dovrebbe avere, quindi ho inserito una richiesta con Microsoft per un gestore degli errori globale per l'API Web :

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

Se sei d'accordo, vai su quel link e votalo!

Nel frattempo, l'eccellente articolo Gestione delle eccezioni dell'API Web ASP.NET mostra alcuni modi diversi per rilevare alcune categorie di errore diverse. È più complicato di quanto dovrebbe essere e non rileva tutti gli errori interal del server, ma è l'approccio migliore disponibile oggi.

Aggiornamento: la gestione degli errori globali è ora implementata e disponibile nelle build notturne! Verrà rilasciato in ASP.NET MVC v5.1. Ecco come funzionerà: https://aspnetwebstack.codeplex.com/wikipage?title=Global%20Error%20Handling


sembra un motivo per usare il controller per le chiamate ajax invece dell'api web .. i confini sono già sfocati .. anche se se ELMAH è in grado di catturarlo, forse c'è un modo
Sonic Soul

4
La gestione degli errori globali è stata ora aggiunta nell'API Web 2.1. Vedi la mia risposta per maggiori dettagli.
decide il

10

È inoltre possibile creare un gestore di eccezioni globale implementando l' IExceptionHandlerinterfaccia (o ereditando la ExceptionHandlerclasse base). Sarà l'ultimo ad essere chiamato nella catena di esecuzione, dopotutto registrato IExceptionLogger:

Il IExceptionHandler gestisce tutte le eccezioni non gestite da tutti i controller. Questo è l'ultimo della lista. Se si verifica un'eccezione, verrà chiamato prima IExceptionLogger, quindi il controller ExceptionFilters e, se ancora non gestita, l'implementazione 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);
        }
    }
}

Maggiori informazioni qui .


Solo curioso, dov'è questo registro?
ThunD3eR

-1

Potresti avere blocchi try-catch esistenti di cui non sei a conoscenza.

Pensavo che il mio nuovo global.asax.Application_Errormetodo non venisse costantemente chiamato per eccezioni non gestite nel nostro codice legacy.

Quindi ho trovato alcuni blocchi try-catch nel mezzo dello stack di chiamate che chiamava Response.Write sul testo dell'eccezione. Questo è tutto. Ho scaricato il testo sullo schermo e poi ucciso la pietra eccezionale.

Quindi le eccezioni venivano gestite, ma la gestione non stava facendo nulla di utile. Una volta rimossi quei blocchi try-catch, le eccezioni si sono propagate al metodo Application_Error come previsto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.