WebApi 控制器被创建,因此构造函数通过 HttpControllerActivators 调用。默认激活器是 System.Web.Http.Dispatcher.DefaultHttpControllerActivator。
github 上选项 1 和 2 的非常粗略的示例https://github.com/markyjones/StackOverflow/tree/master/ControllerExceptionHandling/src
效果很好的选项 1涉及使用 DI 容器(您可能已经在使用一个)。我在示例中使用了 Ninject,并使用“拦截器”阅读更多内容来拦截和尝试/捕获对 DefaultHttpControllerActivator 上的 Create 方法的调用。我至少知道 AutoFac 和 Ninject 可以做与以下类似的事情:
创建拦截器
我不知道您的 Madagascar 和 Log 项目的生命周期范围是多少,但它们很可能被注入到您的拦截器中
public class ControllerCreationInterceptor : Ninject.Extensions.Interception.IInterceptor
{
private ILog _log;
private IMadagascar _madagascar;
public ControllerCreationInterceptor(ILog log, IMadagascar madagascar)
{
_log = log;
_madagascar = madagascar;
}
但请注意您问题中的示例,其中 Log 和 Madagascar 是某种静态全局
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>();
方案二是实现自己的Controller Activator,实现IHttpControllerActivator接口,并在Create方法中处理创建Controller的错误。您可以使用装饰器模式来包装 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; }
}
}