Exception handling 我应该如何处理WebAPI中控制器构造函数中的异常?

Exception handling 我应该如何处理WebAPI中控制器构造函数中的异常?,exception-handling,asp.net-web-api,asp.net-4.5,Exception Handling,Asp.net Web Api,Asp.net 4.5,假设我有一个构造函数,它的初始化可能会由于我无法控制的原因引发异常 FantasticApiController(IAwesomeGenerator awesome, IBusinessRepository repository, IIceCreamFactory factory) { Awesome = awesome; Repository = repository; IceCream = factory.MakeIceCream();

假设我有一个构造函数,它的初始化可能会由于我无法控制的原因引发异常

FantasticApiController(IAwesomeGenerator awesome,
    IBusinessRepository repository, IIceCreamFactory factory)
{
       Awesome = awesome;
       Repository = repository;
       IceCream = factory.MakeIceCream();

       DoSomeInitialization(); // this can throw an exception
}
通常,当WebAPI中的控制器操作引发异常时,我可以通过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;
        });
    }
因此,如果我有一个have a
BoardPlane
API方法抛出一个
bubonicplagueeexception
,那么我的
CustomerErrorHandler
将关闭到马达加斯加的端口,并按预期将其记录为错误。在其他情况下,如果不是很严重,我只会显示一些用户友好的消息,并返回一个500
InternalServerError


但是在那些
DoSomeInitialization
抛出异常的情况下,这绝对不起任何作用如何处理WebAPI控制器构造函数中的异常?

创建WebAPI控制器,并通过HttpControllerActivators调用构造函数。默认激活器是System.Web.Http.Dispatcher.DefaultHttpControllerActivator

这里是github上选项1和2的粗略示例

选项1很好地使用了DI容器(您可能已经在使用一个)。我在我的示例中使用了Ninject,并使用“拦截器”拦截和尝试/捕获对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;
    }
我不知道你的马达加斯加和日志项的生命周期范围是什么,但它们很可能被注入你的拦截器

public class ControllerCreationInterceptor : Ninject.Extensions.Interception.IInterceptor
{
    private ILog _log;
    private IMadagascar _madagascar;

    public ControllerCreationInterceptor(ILog log, IMadagascar madagascar)
    {
        _log = log;
        _madagascar = madagascar;
    }
但请继续你问题中的例子,Log和Madagada是某种静态的全局变量

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)中注册拦截器

拥有自己的自定义激活器后,默认激活器可以位于全局asax中:

  GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new YourCustomControllerActivator());
选项3当然,如果您在构造函数中的初始化不需要访问实际控制器方法、属性等。。。i、 假设它可以从构造函数中删除。。。然后,只需将初始化移动到过滤器(例如

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


}

WebApi的一个有趣特性是,您可以轻松自定义异常返回到客户端的方式。同一操作方法中的状态和Html错误。显然,如果在构造函数中抛出异常,您将失去所有这些。我认为你应该避免这种情况发生。大多数逻辑应该放在webapi方法中,而不是构造函数中。也就是说,我认为您应该使用标准的asp.net错误处理,即在Web.Config中配置错误页面,或者在全局asax中拦截onerror事件
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; }
    }


}