Kontrolery WebApi są tworzone, a zatem konstruktory wywoływane za pośrednictwem HttpControllerActivators. Domyślnym aktywatorem jest System.Web.Http.Dispatcher.DefaultHttpControllerActivator.
Bardzo szorstki przykłady dla opcji 1 & 2 na github tutaj https://github.com/markyjones/StackOverflow/tree/master/ControllerExceptionHandling/src
Wariant 1 który działa całkiem dobrze wiąże się z wykorzystaniem kontenera DI (można używać jednego już dobrze). Użyłem Ninject do mojego przykładu i użyłem "Interceptorów" Read More do przechwytywania i próbowania/wychwytywania wywołań do metody Create na DefaultHttpControllerActivator. Znam przynajmniej AutoFac i Ninject że można zrobić coś podobny do przykładu z poniższym:
Tworzenie przechwytywacza
nie wiem co zakres żywotność swojej Madagaskarze oraz pozycje dziennika są jednak mogli dobrze być wstrzykiwany do swojego Interceptor
public class ControllerCreationInterceptor : Ninject.Extensions.Interception.IInterceptor
{
private ILog _log;
private IMadagascar _madagascar;
public ControllerCreationInterceptor(ILog log, IMadagascar madagascar)
{
_log = log;
_madagascar = madagascar;
}
Ale trzymając się przykładowo w swoim pytaniu gdzie Log i Madagaskar są jakieś statyczne globalnego
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!
}
}
}
WRESZCIE Zarejestruj przechwytywacza w Global asax lub App_Start (NinjectWebCommon)
kernel.Bind<System.Web.Http.Dispatcher.IHttpControllerActivator>()
.To<System.Web.Http.Dispatcher.DefaultHttpControllerActivator>().Intercept().With<ControllerCreationInterceptor>();
Opcja 2 jest wdrożenie własnego kontrolera Activator wykonawczych interfejs IHttpControllerActivator i obsługi błędu w tworzeniu sterownika w Utwórz metodę.Można użyć deseniu dekorator zawinąć 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;
}
}
}
Gdy masz swój własny aktywator domyślny aktywator może być switched out w globalnej asax:
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new YourCustomControllerActivator());
Option 3 Oczywiście jeśli Twoja inicjalizacja w konstruktorze nie wymaga dostępu do rzeczywistych metod, właściwości itd. Kontrolera, tzn. zakładając, że można go usunąć z konstruktora ... wtedy znacznie łatwiej byłoby po prostu przenieść inicjalizację do filtra np.
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; }
}
}
Jedną z interesujących funkcji WebApi jest to, że można łatwo dostosować sposób, w jaki wyjątek jest zwracany do klienta. Błąd statusu i HTML w ramach tej samej metody działania. Oczywiście stracisz wszystko, jeśli wyjątek zostanie zgłoszony w konstruktorze. Myślę, że powinieneś tego unikać. Większość logiki należy umieścić w metodzie webapi, a nie w konstruktorze. Powiedziałem, że powinieneś użyć standardowej obsługi błędów asp.net, czyli konfigurowania stron błędów w Web.Config, lub przechwytywania zdarzenia onerror w globalnym asaxie –