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

¿Cómo atrapo todas las excepciones no controladas que ocurren en ASP.NET Web Api para poder registrarlas?

Hasta ahora lo he intentado:

  • Crear y registrar un ExceptionHandlingAttribute
  • Implementar un método Application_Error en Global.asax.cs
  • Suscribirse a AppDomain.CurrentDomain.UnhandledException
  • Suscribirse a TaskScheduler.UnobservedTaskException

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

  • Se lanzan excepciones cuando un IQueryable devuelto por un método de 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 causar que se devuelva un error interno del servidor 500, quiero que se registre. La implementación de Application_Error hizo bien este trabajo en Web Forms y MVC: ¿qué puedo usar en Web Api?

    Esto ahora es posible con WebAPI 2.1 (vea las 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 callback de configuración como esta:

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

    o directamente:

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

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

    El manejo de todas las excepciones que causan errores internos del servidor parece ser una capacidad básica que la API Web debería tener, así que solicité a Microsoft un controlador de error global 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 ASP.NET Web API Exception Handling 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 interal, pero es el mejor enfoque disponible en la actualidad.

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

    La respuesta de Yuval es para personalizar las respuestas a las excepciones no controladas capturadas por la API web, no para el registro, como se indica en la página enlazada. Consulte la sección Cuándo usar en la página para más detalles. El registrador siempre se llama, pero el controlador solo se invoca 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 ensamblado v5.2.3 y la clase ExceptionHandler no tiene el método HandleCore . El equivalente, creo, es Handle . Sin embargo, simplemente subclases ExceptionHandler (como en la respuesta de Yuval) no funciona. En mi caso, tengo que implementar IExceptionHandler siguiente manera.

     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(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, sin agregar.

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

    También puede crear un controlador de excepción global implementando la interfaz IExceptionHandler (o heredando la clase base de ExceptionHandler ). Será el último en ser llamado en la cadena de ejecución, después de todo IExceptionLogger registrado:

    IExceptionHandler maneja todas las excepciones no controladas de todos los controladores. Este es el último en la lista. Si se produce una excepción, primero se llamará a IExceptionLogger, luego al controlador ExceptionFilters y, si aún no se lo ha manejado, 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 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í .

    Creo que mi nuevo método global.asax.Application_Error no se llama constantemente para excepciones no controladas en nuestro código heredado.

    Luego encontré algunos bloques try-catch en medio de la stack de llamadas que llamaban 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 muerta.