Say I have a constructor where it’s initialization can potentially throw an exception due to reasons beyond my control.
FantasticApiController(IAwesomeGenerator awesome,
IBusinessRepository repository, IIceCreamFactory factory)
{
Awesome = awesome;
Repository = repository;
IceCream = factory.MakeIceCream();
DoSomeInitialization(); // this can throw an exception
}
Ordinarily, when a Controller action in WebAPI throws an exception I can handle it via a csutom ExceptionFilterAttribute:
public class CustomErrorHandler
{
public override void OnException(HttpActionExecutedContext context)
{
// Critical error, this is real bad.
if (context.Exception is BubonicPlagueException)
{
Log.Error(context.Exception, "CLOSE EVERYTHING!");
Madagascar.ShutdownAllPorts();
}
// No big deal, just show something user friendly
throw new HttpResponseException(new HttpResponseMessage
{
Content = new StringContent("Hey something bad happened. " +
"Not closing the ports though"),
StatusCode = HttpStatusCode.InternalServerError;
});
}
So if I have a have a BoardPlane API method which throws a BubonicPlagueException, then my CustomerErrorHandler will shut down the ports to Madagascar and log it as an error as expected. In other instances when it’s not really serious, I just display some user friendly message and return a 500 InternalServerError.
But in those cases where DoSomeInitialization throws an exception, this does absolutely nothing. How can I handle exceptions in WebAPI controller constructors?
The WebApi Controllers are created, and thus constructors called via HttpControllerActivators. The default activator is System.Web.Http.Dispatcher.DefaultHttpControllerActivator.
Very rough examples for options 1 & 2 on github here https://github.com/markyjones/StackOverflow/tree/master/ControllerExceptionHandling/src
Option 1 which works quite nicely involves the use of a DI container (you may well be using one already). I have used Ninject for my example and have used “Interceptors” Read More to intercept and try/catch calls to the Create method on the DefaultHttpControllerActivator. I know of at least AutoFac and Ninject that can do something simlar to to the following:
Create the interceptor
I don’t know what the lifetime scope of your Madagascar and Log items are but they could well be injected into your Interceptor
But keeping to the example in your question where Log and Madagascar are some kind of Static global
FINALLY Register the interceptor In global asax or App_Start (NinjectWebCommon)
Option 2 is to implement your own Controller Activator implementing the IHttpControllerActivator interface and handle the error in creation of the Controller in the Create method. You could use the decorator pattern to wrap the DefaultHttpControllerActivator:
Once you have your own custom activator the default activator can be switched out in the global asax :
Option 3 Of course if your initialisation in the constructor doesn’t need access to the actual Controllers methods, properties etc… i.e. assuming it could be removed from the constructor… then it would be far easier to just move the initialisation to a filter e.g.