异常处理 – 如何处理WebAPI中的控制器构造函数中的异常?

我有一个构造函数,它的初始化可能会由于我无法控制的原因而抛出异常.
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控制器构造函数中的异常?

解决方法

创建WebApi控制器,因此通过HttpControllerActivators调用构造函数.认的激活器是System.Web.Http.dispatcher.DefaultHttpControllerActivator.

选项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; }
    }


}

相关文章

### 创建一个gRPC服务项目(grpc服务端)和一个 webapi项目(...
一、SiganlR 使用的协议类型 1.websocket即时通讯协议 2.Ser...
.Net 6 WebApi 项目 在Linux系统上 打包成Docker镜像,发布为...
一、 PD简介PowerDesigner 是一个集所有现代建模技术于一身的...
一、存储过程 存储过程就像数据库中运行的方法(函数) 优点:...
一、Ueditor的下载 1、百度编辑器下载地址:http://ueditor....