I controller WebApi vengono creati e quindi i costruttori chiamati tramite HttpControllerActivators. L'attivatore predefinito è System.Web.Http.Dispatcher.DefaultHttpControllerActivator.
esempi molto grezzi per le opzioni 1 & 2 su GitHub qui https://github.com/markyjones/StackOverflow/tree/master/ControllerExceptionHandling/src
Opzione 1 che funziona abbastanza bene implica l'uso di un contenitore DI (si può ben essere utilizzato uno già). Ho usato Ninject per il mio esempio e ho usato "Interceptors" Read More per intercettare e provare/catturare le chiamate al metodo Create sul DefaultHttpControllerActivator. So di almeno autofac e Ninject che può fare qualcosa per simlar al seguente:
Creare l'intercettore
Non so che cosa la portata durata del vostro Madagascar e vuoto Log sono ma potrebbero ben essere iniettato nel vostro Interceptor
public class ControllerCreationInterceptor : Ninject.Extensions.Interception.IInterceptor
{
private ILog _log;
private IMadagascar _madagascar;
public ControllerCreationInterceptor(ILog log, IMadagascar madagascar)
{
_log = log;
_madagascar = madagascar;
}
Ma mantenendo l'esempio nella tua domanda dove Log e Madagascar sono una sorta di statico globale
public class ControllerCreationInterceptor : Ninject.Extensions.Interception.IInterceptor
{
public void Intercept(Ninject.Extensions.Interception.IInvocation invocation)
{
try
{
invocation.Proceed();
}
catch(InvalidOperationException e)
{
if (e.InnerException is BubonicPlagueException)
{
Log.Error(e.InnerException, "CLOSE EVERYTHING!");
Madagascar.ShutdownAllPorts();
//DO SOMETHING WITH THE ORIGIONAL ERROR!
}
//DO SOMETHING WITH THE ORIGIONAL ERROR!
}
}
}
FINALMENTE Registrare l'intercettore In ASax globale o App_Start (NinjectWebCommon)
kernel.Bind<System.Web.Http.Dispatcher.IHttpControllerActivator>()
.To<System.Web.Http.Dispatcher.DefaultHttpControllerActivator>().Intercept().With<ControllerCreationInterceptor>();
Opzione 2 è quello di implementare il proprio controller Activator implementando l'interfaccia IHttpControllerActivator e gestire l'errore nella creazione del controller nella Creare un metodoÈ possibile utilizzare il modello decorator per avvolgere la DefaultHttpControllerActivator:
public class YourCustomControllerActivator : IHttpControllerActivator
{
private readonly IHttpControllerActivator _default = new DefaultHttpControllerActivator();
public YourCustomControllerActivator()
{
}
public System.Web.Http.Controllers.IHttpController Create(System.Net.Http.HttpRequestMessage request, System.Web.Http.Controllers.HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
try
{
return _default.Create(request, controllerDescriptor, controllerType);
}
catch (InvalidOperationException e)
{
if (e.InnerException is BubonicPlagueException)
{
Log.Error(e.InnerException, "CLOSE EVERYTHING!");
Madagascar.ShutdownAllPorts();
//DO SOMETHING WITH THE ORIGIONAL ERROR!
}
//DO SOMETHING WITH THE ORIGIONAL ERROR!
return null;
}
}
}
Una volta che avete la vostra attivatore personalizzato l'attivatore di default può essere switched out nel ASax globale:
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new YourCustomControllerActivator());
Opzione 3 Naturalmente se la tua inizializzazione nel costruttore non ha bisogno di accedere ai metodi, alle proprietà ecc. dei controllori attuali ... cioè supponendo che possa essere rimossa dal costruttore ... allora sarebbe molto più semplice spostare semplicemente l'inizializzazione su un filtro, ad es.
public class MadagascarFilter : AbstractActionFilter
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
try{
DoSomeInitialization(); // this can throw an exception
}
catch(BubonicPlagueException e){
Log.Error(e, "CLOSE EVERYTHING!");
Madagascar.ShutdownAllPorts();
//DO SOMETHING WITH THE ERROR
}
base.OnActionExecuting(actionContext);
}
public override void OnActionExecuted(System.Web.Http.Filters.HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
}
public override bool AllowMultiple
{
get { return false; }
}
}
Una caratteristica interessante di WebApi è che è possibile personalizzare facilmente il modo in cui l'eccezione viene restituita al client. Errore di stato e Html all'interno dello stesso metodo di azione. Ovviamente perdi tutto questo se viene lanciata un'eccezione all'interno del costruttore. Penso che dovresti evitare che ciò accada. La maggior parte della logica dovrebbe essere inserita nel metodo webapi non nel costruttore. Detto questo penso che dovresti usare la gestione degli errori standard di asp.net, ovvero configurare le pagine di errore all'interno di Web.Config, o intercettare l'evento onerror nell'asax globale –