Désactiver * toutes * la gestion des exceptions dans ASP.NET Web API 2 (pour faire de la place pour le mien)?

Je veux câbler la gestion des exceptions dans un composant middleware, comme ceci:

public override async Task Invoke(IOwinContext context) { try { await Next.Invoke(context); } catch (Exception ex) { // Log error and return 500 response } } 

Cependant, certaines des exceptions que je voudrais intercepter sont interceptées et converties en HttpErrorResponse s par le pipeline d’API Web avant que je puisse y accéder. En cours de route, je perds beaucoup de détails sur les erreurs, donc je ne peux pas obtenir de traces de stack utiles lors du débogage, etc. (le débogueur ne s’arrête même pas lorsque l’exception est lancée – où il échoue …).

J’ai essayé d’append un gestionnaire d’exceptions personnalisé avec l’implémentation suivante:

 public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken) { var owinContext = context.Request.GetOwinContext(); owinContext.Set(Constants.ContextKeys.Exception, context.Exception); return Task.FromResult(0); } 

enregistré via config.Services.Replace(typeof(IExceptionHandler), new MyExceptionHandler()); dans ma configuration de démarrage, mais en l’examinant après l’exécution de Next.Invoke(context) via

 context.Get(Constants.ContextKeys.Exception); 

ne me donne toujours pas tous les détails que je veux, ainsi que de ne pas m’arrêter au sharepoint défaillance avec le débogueur.

Existe-t-il un moyen de désactiver complètement la gestion des erreurs intégrée afin que mon propre middleware puisse s’en charger?

Clarification , car beaucoup de gens semblent mal comprendre ce que je cherche:

  • La gestion des erreurs intégrée dans Web API intercepte certaines exceptions (mais pas toutes) et les réécrit en 500 réponses.
  • Je veux attraper toutes les exceptions, faire un peu de journalisation, puis émettre 500 réponses avec les informations que je choisis (pour la plupart, voir le point suivant).
  • Il existe également des exceptions qui signalent des erreurs de logique métier, pour lesquelles je souhaite renvoyer des erreurs 40x.
  • Je veux que cela soit en haut du pipeline (app), c’est-à-dire en encapsulant tout le rest dans le cycle de vie de la requête
  • Je veux gérer cela en utilisant OWIN, pour le rendre portable à un éventuel futur scénario auto-hébergé (c’est-à-dire qu’il n’est pas écrit que cette application sera toujours hébergée sur les modules IIS – HTTP, Global.asax.cs et al ne sont pas pertinents ici).

Mise à jour : j’ai blogué à ce sujet . Lors de la recherche de l’article sur le blog, j’ai trouvé un potentiel d’amélioration. J’ai mis à jour les parties pertinentes de cette réponse. Pour plus de détails sur les raisons pour lesquelles je pense que c’est mieux que toutes les autres suggestions ici, ou le comportement par défaut, lisez l’intégralité du message 🙂


Je suis maintenant allé avec l’approche suivante, qui semble fonctionner correctement, même si elle n’est pas à 100% conforme à ce que je cherchais:

  • Créez une classe 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; } } 
  • Laissez cette classe remplacer le service IExceptionHandler de Web API:

     config.Services.Replace(typeof(IExceptionHandler), new PassthroughExceptionHandler()); 
  • Créez un cours de middleware qui fait ce que je veux:

     public class ExceptionHandlerMiddleware { public override async Task Invoke(IOwinContext context) { try { await Next?.Invoke(context); } catch (Exception ex) { // handle and/or log } } } 
  • Enregistrez d’abord ce middleware dans la stack:

     app.Use() .UseStageMarker(PipelineStage.Authenticate) // other middlewares omitted for brevity .UseStageMarker(PipelineStage.PreHandlerExecute) .UseWebApi(config); 

Je vais quand même atsortingbuer la prime à toute personne qui arrive (la prime a expiré …) Je suis toujours à la recherche d’une meilleure solution, qui, par exemple, casse une exception non gérée. (Cette approche fait que VS se casse lorsque je retransmet l’exception dans le gestionnaire, mais la stack d’appel d’origine est perdue; je dois définir un point d’arrêt sur la ligne défaillante et déboguer à nouveau pour pouvoir intercepter l’état lorsqu’une exception est levée.)

Je ne sais pas si cela fonctionnera pour vous, mais j’ai une exigence similaire pour renvoyer toutes les erreurs en tant que JSON, même pour les erreurs non trouvées. J’ai créé un contrôleur de base et j’ai remplacé ExecuteAsync, ce qui m’a permis de créer mes propres réponses.

 public class ControllerBase : ApiController { protected ssortingng ClassName = "ControllerBase::"; public override System.Threading.Tasks.Task ExecuteAsync(System.Web.Http.Controllers.HttpControllerContext controllerContext, System.Threading.CancellationToken cancellationToken) { try { System.Threading.Tasks.Task 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 = ssortingng.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(() => ErrorResponse); } return TaskList; } catch (Exception Error) { JSONErrorResponse BadParameters = new JSONErrorResponse(); BadParameters.ExceptionMessage = Error.Message; BadParameters.ErrorMessage = ssortingng.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(() => ErrorResponse); } } } public class JSONErrorResponse { //Possible message from exception public ssortingng ExceptionMessage { get; set; } //Possible custom error message public ssortingng ErrorMessage { get; set; } //Http error code public HttpStatusCode HttpErrorCode { get; set; } } 

Vous pouvez également essayer de créer votre propre activateur de contrôleur, avoir votre gestionnaire d’exceptions personnalisé et essayer d’utiliser ExceptionFilterAtsortingbute.

  1. Créez votre activateur de contrôleur

     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))); } } } 
  2. Créer une exceptionFilterAtsortingbute

     public class ExceptionHandlingFilter : ExceptionFilterAtsortingbute { 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); } } 
  3. ResponseModel est une classe que je sérialise en JSON en utilisant Formatters et renvoie par toutes les réponses du contrôleur, ainsi le client est capable d’identifier les données d’erreur ainsi qu’une réponse réussie en plus du code d’état HTTP.

     config.Formatters.Clear(); // do not need any other config.Formatters.Add(new JsonMediaTypeFormatter()); 
  4. Câbler

     // ... [cut] ... config.Filters.Add(new ExceptionHandlingFilter()); // ... [cut] ... config.Services.Replace(typeof(IHttpControllerActivator), new ExceptionHandlingControllerActivator(config.Services.GetHttpControllerActivator()) ); // ... [cut] ... app.UseWebApi(config); 

OWIN n’est pas censé gérer les exceptions de ce type, car l’API Web a sa propre gestion des erreurs intégrée. OWIN est conçu pour être distinct de l’application. Si vous définissez un point d’arrêt sur la méthode HandleAsync de votre gestionnaire d’exceptions, vous devriez pouvoir inspecter la variable de contexte et voir les détails de l’exception.

Si vous essayez de le faire uniquement à des fins de débogage, la définition de votre point d’arrêt doit vous permettre de voir l’exception. Si vous devez enregistrer les exceptions, le gestionnaire d’exceptions est le meilleur endroit pour le faire, à mon avis.

J’espère que cela pourra aider.

Voici quelque chose qui pourrait aider:

https://stackoverflow.com/a/21382651/1419853

http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-21#global-error

Essentiellement, il existe un support intégré pour intercepter, gérer et modifier les erreurs.

Cela ressemble à ceci:

 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.ToSsortingng()); } }