How comes that a custom ExceptionHandler
is never called and instead a standard response (not the one I want) is returned?
Registered like this
config.Services.Add(typeof(IExceptionLogger), new ElmahExceptionLogger()); config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
and implemented like this
public class GlobalExceptionHandler : ExceptionHandler { public override void Handle(ExceptionHandlerContext context) { context.Result = new ExceptionResponse { statusCode = context.Exception is SecurityException ? HttpStatusCode.Unauthorized : HttpStatusCode.InternalServerError, message = "An internal exception occurred. We'll take care of it.", request = context.Request }; } } public class ExceptionResponse : IHttpActionResult { public HttpStatusCode statusCode { get; set; } public string message { get; set; } public HttpRequestMessage request { get; set; } public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) { var response = new HttpResponseMessage(statusCode); response.RequestMessage = request; response.Content = new StringContent(message); return Task.FromResult(response); } }
and thrown like this (test)
throw new NullReferenceException("testerror");
in a controller or in a repository.
UPDATE
I do not have another ExceptionFilter
.
I found a trigger for this behavior:
Given URL
GET http://localhost:XXXXX/template/lock/someId
sending this header, my ExceptionHandler
works
Host: localhost:XXXXX
sending this header, it doesn't work and the built-in handler returns the error instead
Host: localhost:XXXXX Origin: http://localhost:YYYY
This might be an issue with CORS requests (I use the WebAPI CORS package globally with wildcards) or eventually my ELMAH logger. It also happens when hosted on Azure (Websites), though the built-in error handler is different.
Any idea how to fix this?
Using Exception Filters Exception filters can be used to handle unhandled exceptions which are generated in Web API. The exception filter can be able to catch the unhandled exceptions in Web API. This filter is executed when an action method throws the unhandled exception.
To apply the filter globally to all Web API controllers, add an instance of the filter to the GlobalConfiguration. Configuration. Filters collection. Exception filters in this collection apply to any Web API controller action.
By default, most exceptions are translated into an HTTP response with status code 500, Internal Server Error.
Turns out the default only handles outermost exceptions, not exceptions in repository classes. So below has to be overridden as well:
public virtual bool ShouldHandle(ExceptionHandlerContext context) { return context.ExceptionContext.IsOutermostCatchBlock; }
UPDATE 1
WebAPI v2 does not use IsOutermostCatchBlock
anymore. Anyway nothing changes in my implementation, since the new code in ShouldHandle
still prevents my Error Handler. So I'm using this and my Error Handler gets called once. I catch errors in Controllers and Repositories this way.
public virtual bool ShouldHandle(ExceptionHandlerContext context) { return true; }
UPDATE 2
Since this question got so much attention, please be aware that the current solution is the one linked by @JustAMartin in the comments below.
The real culprit here is CorsMessageHandler inserted by EnableCors method in message processing pipline. The catch block intercept any exception and convert into a response before it can reach the HTTPServer try-catch block and ExceptionHandler logic can be invoked
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { CorsRequestContext corsRequestContext = request.GetCorsRequestContext(); HttpResponseMessage result; if (corsRequestContext != null) { try { if (corsRequestContext.IsPreflight) { result = await this.HandleCorsPreflightRequestAsync(request, corsRequestContext, cancellationToken); return result; } result = await this.HandleCorsRequestAsync(request, corsRequestContext, cancellationToken); return result; } catch (Exception exception) { result = CorsMessageHandler.HandleException(request, exception); return result; } } result = await this.<>n__FabricatedMethod3(request, cancellationToken); return result; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With