Ответ 1
Создаются контроллеры WebApi и, следовательно, конструкторы, вызываемые через HttpControllerActivators. Активатором по умолчанию является System.Web.Http.Dispatcher.DefaultHttpControllerActivator.
Очень грубые примеры для вариантов 1 и 2 на github здесь https://github.com/markyjones/StackOverflow/tree/master/ControllerExceptionHandling/src
Вариант 1, который работает довольно хорошо, подразумевает использование контейнера DI (вы можете использовать его уже). Я использовал Ninject для моего примера и использовал "Перехватчики" Подробнее, чтобы перехватить и попробовать/уловить вызовы метода Create в DefaultHttpControllerActivator. Я знаю, по крайней мере, AutoFac и Ninject, которые могут сделать что-то похожее на следующее:
Создайте перехватчик
Я не знаю, какова продолжительность жизни ваших мадагаскаров и элементов журнала, но они вполне могут быть введены в ваш Interceptor
public class ControllerCreationInterceptor : Ninject.Extensions.Interception.IInterceptor
{
private ILog _log;
private IMadagascar _madagascar;
public ControllerCreationInterceptor(ILog log, IMadagascar madagascar)
{
_log = log;
_madagascar = madagascar;
}
Но придерживаясь примера в вашем вопросе, где Log и Мадагаскар являются своего рода статическим глобальным
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!
}
}
}
ЗАКЛЮЧИТЕЛЬНО Зарегистрируйте перехватчик В глобальном asax или App_Start (NinjectWebCommon)
kernel.Bind<System.Web.Http.Dispatcher.IHttpControllerActivator>()
.To<System.Web.Http.Dispatcher.DefaultHttpControllerActivator>().Intercept().With<ControllerCreationInterceptor>();
Вариант 2 заключается в том, чтобы реализовать собственный Activator Activator, реализующий интерфейс IHttpControllerActivator, и обрабатывать ошибку при создании контроллера в методе Create. Вы можете использовать шаблон декоратора, чтобы обернуть 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;
}
}
}
Когда у вас есть собственный пользовательский активатор, активатор по умолчанию может отключить в глобальном asax:
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new YourCustomControllerActivator());
Вариант 3 Конечно, если ваша инициализация в конструкторе не нуждается в доступе к действительным методам контроллеров, свойствам и т.д., т.е. предполагается, что он может быть удален из конструктора... тогда он было бы намного проще просто перенести инициализацию на фильтр, например
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; }
}
}