catch all unhandled exceptions in ASP.NET Web Api
Solution 1
This is now possible with WebAPI 2.1 (see the What's New):
Create one or more implementations of IExceptionLogger. For example:
public class TraceExceptionLogger : ExceptionLogger
{
public override void Log(ExceptionLoggerContext context)
{
Trace.TraceError(context.ExceptionContext.Exception.ToString());
}
}
Then register with your application's HttpConfiguration, inside a config callback like so:
config.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());
or directly:
GlobalConfiguration.Configuration.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());
Solution 2
To answer my own question, this isn't possible!
Handling all exceptions that cause internal server errors seems like a basic capability Web API should have, so I have put in a request with Microsoft for a Global error handler for Web API:
https://aspnetwebstack.codeplex.com/workitem/1001
If you agree, go to that link and vote for it!
In the meantime, the excellent article ASP.NET Web API Exception Handling shows a few different ways to catch a few different categories of error. It's more complicated than it should be, and it doesn't catch all interal server errors, but it's the best approach available today.
Update: Global error handling is now implemented and available in the nightly builds! It will be released in ASP.NET MVC v5.1. Here's how it will work: https://aspnetwebstack.codeplex.com/wikipage?title=Global%20Error%20Handling
Solution 3
The Yuval's answer is for customizing responses to unhandled exceptions caught by Web API, not for logging, as noted on the linked page. Refer to the When to Use section on the page for details. The logger is always called but the handler is called only when a response can be sent. In short, use the logger to log and the handler to customize the response.
By the way, I am using assembly v5.2.3 and the ExceptionHandler
class does not have the HandleCore
method. The equivalent, I think, is Handle
. However, simply subclassing ExceptionHandler
(as in Yuval's answer) does not work. In my case, I have to implement IExceptionHandler
as follows.
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);
}
}
Note that, unlike the logger, you register your handler by replacing the default handler, not adding.
config.Services.Replace(typeof(IExceptionHandler),
new OopsExceptionHandler(config.Services.GetExceptionHandler()));
Solution 4
You can also create a global exception handler by implementing the IExceptionHandler
interface (or inherit the ExceptionHandler
base class). It will be the last to be called in the execution chain, after all registered IExceptionLogger
:
The IExceptionHandler handles all unhandled exceptions from all controllers. This is the last in the list. If an exception occurs, the IExceptionLogger will be called first, then the controller ExceptionFilters and if still unhandled, the IExceptionHandler implementation.
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);
}
}
}
More on that here.
user3156301
Updated on May 12, 2020Comments
-
user3156301 over 3 years
How do I catch all unhandled exceptions that occur in ASP.NET Web Api so that I can log them?
So far I have tried:
- Create and register an
ExceptionHandlingAttribute
- Implement an
Application_Error
method inGlobal.asax.cs
- Subscribe to
AppDomain.CurrentDomain.UnhandledException
- Subscribe to
TaskScheduler.UnobservedTaskException
The
ExceptionHandlingAttribute
successfully handles exceptions that are thrown within controller action methods and action filters, but other exceptions are not handled, for example:- Exceptions thrown when an
IQueryable
returned by an action method fails to execute - Exceptions thrown by a message handler (i.e.
HttpConfiguration.MessageHandlers
) - Exceptions thrown when creating a controller instance
Basically, if an exception is going to cause a 500 Internal Server Error to be returned to the client, I want it logged. Implementing
Application_Error
did this job well in Web Forms and MVC - what can I use in Web Api? - Create and register an
-
Sonic Soul about 10 yearsseems like a reason to use controller for ajax calls instead of web api.. the boundaries are already blurry.. although if ELMAH is able to capture it, maybe there is a way
-
decates almost 10 yearsGlobal error handling now been added in Web API 2.1. See my answer for more details.
-
decates over 9 years@NeilBarnwell Yes, Web API 2.1 corresponds to System.Web.Http assembly version 5.1.0. So you need this version or above to use the solution described here. See the nuget package versions
-
Avner over 8 yearsCertain 500 errors still don't get caught by this, eg. HttpException - the remote host closed the connection. Is there still a place for global.asax Application_Error to handle errors outside web api processing?
-
Rajiv almost 7 yearsThis is a great solution, this should be the accepted solution to 'catch or log all errors'. I could have never figured out what why it didn't work for me when I was just extending ExceptionHandler.
-
Gustyn almost 7 yearsGreat solution. Once the MVC pipeline has been loaded for a request this works great. IIS still handles the exceptions until then, including when spinning up OWIN in startup.cs. However, at some point after spin up finishes processing startup.cs it does it's job wonderfully.
-
Rocklan over 6 yearsI love how detailed the official doco is on msdn and what 99% of developers really want is just the 8 lines of code to log errors.
-
ThunD3eR over 3 yearsJust curious, where is this registerd?
-
Robert almost 3 yearsI'm sure it still doesn't catch all the 500 errors: for example, if an error is thrown inside the Invoke method of a custom Owin Middleware!
-
Kevin Dimey over 2 yearsHandler can be registered with your dependency injection framework