captura todas las excepciones no controladas en ASP.NET Web Api

113

¿Cómo puedo detectar todas las excepciones no controladas que se producen en ASP.NET Web Api para poder registrarlas?

Hasta ahora lo he intentado:

  • Crea y registra un ExceptionHandlingAttribute
  • Implementar un Application_Errormétodo enGlobal.asax.cs
  • Suscribirse a AppDomain.CurrentDomain.UnhandledException
  • Suscribirse a TaskScheduler.UnobservedTaskException

El ExceptionHandlingAttributemaneja con éxito las excepciones que se lanzan dentro de los métodos de acción del controlador y los filtros de acción, pero no se manejan otras excepciones, por ejemplo:

  • Excepciones lanzadas cuando un IQueryablemétodo devuelto por una acción no se ejecuta
  • Excepciones lanzadas por un controlador de mensajes (es decir HttpConfiguration.MessageHandlers)
  • Excepciones lanzadas al crear una instancia de controlador

Básicamente, si una excepción va a provocar que se devuelva al cliente un error interno del servidor 500, quiero que se registre. La implementación Application_Errorhizo bien este trabajo en Web Forms y MVC: ¿qué puedo usar en Web Api?

Joe Daley
fuente
¿Ha intentado utilizar ASP.NET Health Monitoring ? Solo habilítelo y vea si sus excepciones no se registran en el registro de eventos.
John Saunders
Health Monitoring detecta mis excepciones de canalización de MVC, pero no mis excepciones de canalización de Web Api.
Joe Daley
Gracias - Me tomó un tiempo descubrir por qué no podía registrar mis problemas de inyección de constructor / dependencia, donde pensé que ya tenía el registro de WebAPI ordenado ...
Sobrevuelo

Respuestas:

156

Esto ahora es posible con WebAPI 2.1 (consulte Novedades ):

Cree una o más implementaciones de IExceptionLogger. Por ejemplo:

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

Luego, regístrese con HttpConfiguration de su aplicación, dentro de una devolución de llamada de configuración así:

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

o directamente:

GlobalConfiguration.Configuration.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());
decae
fuente
7
@NeilBarnwell Sí, Web API 2.1 corresponde a la versión de ensamblado System.Web.Http 5.1.0 . Por lo tanto, necesita esta versión o una superior para utilizar la solución que se describe aquí. Vea las versiones del paquete nuget
decates
2
Ciertos 500 errores aún no quedan atrapados por esto, por ejemplo. HttpException: el host remoto cerró la conexión. ¿Todavía hay un lugar para que global.asax Application_Error maneje errores fuera del procesamiento de la API web?
Avner
11
Me encanta lo detallado que es el documento oficial en msdn y lo que el 99% de los desarrolladores realmente quieren son solo las 8 líneas de código para registrar errores.
Rocklan
20

La respuesta de Yuval es para personalizar las respuestas a excepciones no controladas capturadas por la API web, no para el registro, como se indica en la página vinculada . Consulte la sección Cuándo usar en la página para obtener más detalles. Siempre se llama al registrador, pero solo se llama al controlador cuando se puede enviar una respuesta. En resumen, use el registrador para iniciar sesión y el controlador para personalizar la respuesta.

Por cierto, estoy usando el ensamblaje v5.2.3 y la ExceptionHandlerclase no tiene el HandleCoremétodo. El equivalente, creo, es Handle. Sin embargo, simplemente la subclasificación ExceptionHandler(como en la respuesta de Yuval) no funciona. En mi caso, tengo que implementar IExceptionHandlerlo siguiente.

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

Tenga en cuenta que, a diferencia del registrador, registra su controlador reemplazando el controlador predeterminado, no agregando.

config.Services.Replace(typeof(IExceptionHandler),
    new OopsExceptionHandler(config.Services.GetExceptionHandler()));
Duoc Tran
fuente
1
Esta es una gran solución, esta debería ser la solución aceptada para 'detectar o registrar todos los errores'. Nunca podría haber descubierto por qué no funcionó para mí cuando solo estaba extendiendo ExceptionHandler.
Rajiv
Gran solucion Una vez que se ha cargado la canalización MVC para una solicitud, esto funciona muy bien. IIS todavía maneja las excepciones hasta entonces, incluso cuando se activa OWIN en startup.cs. Sin embargo, en algún momento después de que el giro termine de procesar startup.cs, hace su trabajo maravillosamente.
Gustyn
18

Para responder a mi propia pregunta, ¡esto no es posible!

El manejo de todas las excepciones que causan errores internos del servidor parece una capacidad básica que debería tener la API web, por lo que solicité a Microsoft un controlador de errores globales para la API web :

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

Si está de acuerdo, vaya a ese enlace y vote por él.

Mientras tanto, el excelente artículo Manejo de excepciones de la API web ASP.NET muestra algunas formas diferentes de detectar algunas categorías diferentes de error. Es más complicado de lo que debería ser y no detecta todos los errores del servidor interno, pero es el mejor enfoque disponible en la actualidad.

Actualización: ¡ El manejo de errores globales ahora está implementado y disponible en las compilaciones nocturnas! Se lanzará en ASP.NET MVC v5.1. Así es como funcionará: https://aspnetwebstack.codeplex.com/wikipage?title=Global%20Error%20Handling

Joe Daley
fuente
parece una razón para usar el controlador para llamadas ajax en lugar de la API web ... los límites ya están borrosos ... aunque si ELMAH puede capturarlo, tal vez haya una manera
Sonic Soul
4
El manejo de errores globales ahora se ha agregado en Web API 2.1. Vea mi respuesta para más detalles.
decae el
10

También puede crear un controlador de excepciones global implementando la IExceptionHandlerinterfaz (o heredando la ExceptionHandlerclase base). Será el último en ser llamado en la cadena de ejecución, después de todos registrados IExceptionLogger:

IExceptionHandler maneja todas las excepciones no controladas de todos los controladores. Este es el último de la lista. Si ocurre una excepción, se llamará primero a IExceptionLogger, luego al controlador ExceptionFilters y, si aún no se controla, a la implementación de 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);
        }
    }
}

Más sobre eso aquí .

Yuval Itzchakov
fuente
Solo por curiosidad, ¿dónde está registrado?
ThunD3eR
-1

Es posible que tenga bloques try-catch existentes de los que no tenga conocimiento.

Pensé que mi nuevo global.asax.Application_Errormétodo no estaba siendo llamado constantemente para excepciones no controladas en nuestro código heredado.

Luego encontré algunos bloques try-catch en el medio de la pila de llamadas que llamaba Response.Write en el texto de excepción. Eso fue todo. Volcó el texto en la pantalla y luego mató a la piedra de excepción.

Así que se estaban manejando las excepciones, pero el manejo no estaba haciendo nada útil. Una vez que eliminé esos bloques try-catch, las excepciones se propagaron al método Application_Error como se esperaba.

Recurso
fuente