c# - try - Desactivar*todo*manejo de excepciones en ASP.NET Web API 2(para hacer espacio para el mío)?
personalizar exception c# (5)
Aquí hay algo que podría ayudar:
https://stackoverflow.com/a/21382651/1419853
http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-21#global-error
Básicamente, hay un soporte integrado para capturar, manejar y cambiar errores.
Se ve algo como esto:
public class ExceptionLogger : System.Web.Http.ExceptionHandling.ExceptionLogger
{
Logger _logger;
public ExceptionLogger(Logger logger)
{
_logger = logger;
}
public override void Log(ExceptionLoggerContext context)
{
_logger.Error(context.ExceptionContext.Exception.ToString());
}
}
Quiero conectar el manejo de excepciones en un componente de middleware, algo como esto:
public override async Task Invoke(IOwinContext context)
{
try
{
await Next.Invoke(context);
}
catch (Exception ex)
{
// Log error and return 500 response
}
}
Sin embargo, algunas de las excepciones que me gustaría detectar están siendo capturadas y convertidas a HttpErrorResponse
s por la HttpErrorResponse
de la API web antes de que pueda acceder a ellas. En el proceso, pierdo muchos detalles sobre los errores, por lo que no puedo obtener seguimientos de pila útiles cuando se depura, etc. (el depurador ni siquiera se detiene cuando se lanza la excepción: tengo que pasar manualmente el código y ver donde falla ...).
Intenté agregar un manejador de excepción personalizado con la siguiente implementación:
public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
var owinContext = context.Request.GetOwinContext();
owinContext.Set(Constants.ContextKeys.Exception, context.Exception);
return Task.FromResult(0);
}
registrado a través de config.Services.Replace(typeof(IExceptionHandler), new MyExceptionHandler());
en mi configuración de inicio, pero mirándolo después de ejecutar Next.Invoke(context)
través de
context.Get<Exception>(Constants.ContextKeys.Exception);
todavía no me da todos los detalles que quiero, y tampoco me detengo en el punto de falla con el depurador.
¿Hay alguna manera de poder desactivar por completo todo el manejo de errores incorporado, para que mi propio middleware pueda encargarse de eso?
Aclaración , ya que muchas personas parecen entender mal lo que estoy buscando:
- El manejo de errores incorporado en Web API detecta algunas (pero no todas) las excepciones y las reescribe en 500 respuestas.
- Quiero ver todas las excepciones, hacer un poco de registro y luego emitir 500 respuestas con la información que elijo (para la mayoría de ellas, ver el siguiente punto).
- También hay algunas excepciones que señalan fallas lógicas de negocios, para las cuales quiero devolver errores 40x.
- Quiero que esté en la parte superior de la interconexión (de la aplicación), es decir, que incluya todo lo demás en el ciclo de vida de la solicitud.
- Quiero manejar esto usando OWIN, para hacerlo portátil a un posible escenario autohospedado futuro (es decir, no está escrito en piedra que esta aplicación siempre estará alojada en IIS - Módulos HTTP, Global.asax.cs et al no son relevantes aquí).
No estoy seguro de si esto funcionará para usted, pero tengo un requisito similar de devolver todos los errores como JSON incluso para los errores no encontrados. Creé un controlador base y anulé ExecuteAsync, lo que me permitió crear mis propias respuestas.
public class ControllerBase : ApiController
{
protected string ClassName = "ControllerBase::";
public override System.Threading.Tasks.Task<HttpResponseMessage> ExecuteAsync(System.Web.Http.Controllers.HttpControllerContext controllerContext, System.Threading.CancellationToken cancellationToken)
{
try
{
System.Threading.Tasks.Task<HttpResponseMessage> TaskList = base.ExecuteAsync(controllerContext, cancellationToken);
if (TaskList.Exception != null && TaskList.Exception.GetBaseException() != null)
{
JSONErrorResponse AsyncError = new JSONErrorResponse();
AsyncError.ExceptionMessage = TaskList.Exception.GetBaseException().Message;
AsyncError.ErrorMessage = string.Format("Unknown error {0} ExecuteAsync {1}", ClassName ,controllerContext.Request.RequestUri.AbsolutePath);
AsyncError.HttpErrorCode = HttpStatusCode.BadRequest;
HttpResponseMessage ErrorResponse = controllerContext.Request.CreateResponse(AsyncError.HttpErrorCode, AsyncError);
return System.Threading.Tasks.Task.Run<HttpResponseMessage>(() => ErrorResponse);
}
return TaskList;
}
catch (Exception Error)
{
JSONErrorResponse BadParameters = new JSONErrorResponse();
BadParameters.ExceptionMessage = Error.Message;
BadParameters.ErrorMessage = string.Format("Method [{0}], or URL [{1}] not found, verify your request", controllerContext.Request.Method.Method, controllerContext.Request.RequestUri.AbsolutePath);
BadParameters.HttpErrorCode = HttpStatusCode.NotFound;
HttpResponseMessage ErrorResponse = controllerContext.Request.CreateResponse(BadParameters.HttpErrorCode, BadParameters);
return System.Threading.Tasks.Task.Run<HttpResponseMessage>(() => ErrorResponse);
}
}
}
public class JSONErrorResponse
{
//Possible message from exception
public string ExceptionMessage { get; set; }
//Possible custom error message
public string ErrorMessage { get; set; }
//Http error code
public HttpStatusCode HttpErrorCode { get; set; }
}
Se supone que OWIN no maneja excepciones como esa porque la API web tiene incorporado su propio manejo de errores. OWIN está diseñado para ser independiente de la aplicación. Si establece un punto de interrupción en el método HandleAsync de su manejador de excepciones, debería poder inspeccionar la variable de contexto y ver los detalles de la excepción.
Si está tratando de hacer esto solo con fines de depuración, establecer su punto de interrupción allí debería permitirle ver la excepción. Si necesita registrar las excepciones, el controlador de excepción es el mejor lugar para hacerlo, en mi opinión.
Espero que ayude.
También puede intentar crear su propio activador de controlador, tener su controlador de excepciones personalizado e intentar usar ExceptionFilterAttribute.
Crea tu activador de controlador
public class ExceptionHandlingControllerActivator : IHttpControllerActivator { private readonly IHttpControllerActivator _concreteActivator; public ExceptionHandlingControllerActivator(IHttpControllerActivator concreteActivator) { _concreteActivator = concreteActivator; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { try { return _concreteActivator.Create(request, controllerDescriptor, controllerType); } catch (Exception ex) { // do stuff with the exception throw new HttpResponseException(request.CreateResponse(HttpStatusCode.InternalServerError, new ResponseModel(ex))); } } }
Crear ExceptionFilterAttribute
public class ExceptionHandlingFilter : ExceptionFilterAttribute { public override void OnException(HttpActionExecutedContext context) { // do stuff with the exception var request = context.Request; ResponseModel respMod = null; // Example: if debug constant is not defined, mask exception, otherwise create normal object with message, inner exception and stacktrace #if !DEBUG respMod = new ResponseModel(context.Exception, context.Exception.Message, true); #else respMod = new ResponseModel(context.Exception); #endif context.Response = request.CreateResponse(HttpStatusCode.InternalServerError, respMod); } }
ResponseModel es una clase que serializo a JSON usando formateadores y devuelvo por todas las respuestas del controlador, por lo que el cliente puede identificar los datos de error además de la respuesta exitosa además del código de estado HTTP.
config.Formatters.Clear(); // do not need any other config.Formatters.Add(new JsonMediaTypeFormatter());
Cablear
// ... [cut] ... config.Filters.Add(new ExceptionHandlingFilter()); // ... [cut] ... config.Services.Replace(typeof(IHttpControllerActivator), new ExceptionHandlingControllerActivator(config.Services.GetHttpControllerActivator()) ); // ... [cut] ... app.UseWebApi(config);
Actualización : publiqué sobre esto . Al investigar la publicación del blog, encontré algún potencial de mejora; He actualizado las partes relevantes de esta respuesta. Para obtener más detalles sobre por qué creo que esto es mejor que todas las otras sugerencias aquí, o el comportamiento predeterminado, lea la publicación completa :)
Ahora me he ido con el siguiente enfoque, que parece funcionar bien, incluso si no es 100% compatible con lo que estaba buscando:
Crea una clase
PassthroughExceptionHandler
:public class PassthroughExceptionHandler : IExceptionHandler { public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken) { // don''t just throw the exception; that will ruin the stack trace var info = ExceptionDispatchInfo.Capture(context.Exception); info.Throw(); return Task.CompletedTask; } }
Permita que esa clase reemplace el servicio
IExceptionHandler
de la API web:config.Services.Replace(typeof(IExceptionHandler), new PassthroughExceptionHandler());
Crea una clase de middleware que hace lo que quiero:
public class ExceptionHandlerMiddleware { public override async Task Invoke(IOwinContext context) { try { await Next?.Invoke(context); } catch (Exception ex) { // handle and/or log } } }
Registre ese middleware primero en la pila:
app.Use<ExceptionHandlerMiddleware>() .UseStageMarker(PipelineStage.Authenticate) // other middlewares omitted for brevity .UseStageMarker(PipelineStage.PreHandlerExecute) .UseWebApi(config);
Todavía otorgaré la recompensa a cualquiera que se le ocurra (la recompensa expiró ...) Todavía estoy buscando una solución mejor, que, por ejemplo, se rompa cuando se lanza una excepción no controlada . (Este enfoque hace que VS se rompa cuando vuelvo a lanzar la excepción en el controlador, pero la pila de llamadas original se pierde, tengo que establecer un punto de interrupción en la línea que falla y volver a depurar para poder interceptar el estado cuando se lanza una excepción).