FantasticApiController(IAwesomeGenerator awesome,IBusinessRepository repository,IIceCreamFactory factory) { Awesome = awesome; Repository = repository; IceCream = factory.MakeIceCream(); DoSomeInitialization(); // this can throw an exception }
通常,当WebAPI中的Controller操作抛出异常时,我可以通过一个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; }); }
所以如果我有一个BoardPlane API方法抛出一个BubonicPlagueException,那么我的CustomerErrorHandler将关闭端口到马达加斯加,并按预期的方式将其记录为一个错误.在其他情况下,当它不是很严重时,我只显示一些用户友好的消息并返回500 InternalServerError.
但是在DoSomeInitialization引发异常的情况下,这绝对没有.如何处理WebAPI控制器构造函数中的异常?
解决方法
选项1& 2在github这里https://github.com/markyjones/StackOverflow/tree/master/ControllerExceptionHandling/src
方案1的工作相当不错,涉及使用DI容器(您可能已经使用了一个).我使用Ninject作为我的例子,并使用“Interceptor”Read More拦截并尝试/捕获对DefaultHttpControllerActivator的Create方法的调用.我至少知道AutoFac和Ninject可以做一些类似于以下的事情:
创建拦截器
我不知道你的马达加斯加和日志项目的生命周期是什么,但是他们可以很好地注入到你的拦截器中
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>();
选项2是实现自己的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中的switched out:
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator),new YourCustomControllerActivator());
选项3当然,如果您在构造函数中的初始化不需要访问实际的Controllers方法,属性等,即假设可以从构造函数中删除…那么将初始化变为一个过滤器
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; } } }