在 .NET Web 中捕获控制器中的依赖项注入错误 Api

Catching dependency injection errors in controllers in .NET Web Api

我有一个简单的 C# Web Api 项目,它提供了几个 restful 端点。

控制器致命服务器错误 handling/logging 通常可以通过以下方式很好地描述:

  1. Implementing/overriding Application_Error Global.asax.cs
  2. 中的方法
protected override void Application_Error(object sender, EventArgs e)
   {
       var ex = Server.GetLastError();
       _logger.Error("Unexpected error while initializing application", ex);
   }
  1. 或者通过添加异常处理过滤器:

config.Filters.Add(new ExceptionHandlingAttribute());

GlobalConfiguration.Configuration.Filters.Add(new ExceptionHandlingAttribute());

public class ExceptionHandlingAttribute : ExceptionFilterAttribute
    {
        private static readonly ILog _logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        public override void OnException(HttpActionExecutedContext actionExecutedContext)
        {
            _logger.Error("Unexpected error in API.", actionExecutedContext.Exception);

            throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
            {
                Content = new StringContent("An error occurred, please try again or contact the administrator."),
                ReasonPhrase = "Critical Exception"
            });
        }
    }

但是,当在控制器实例化期间由于此代码的构造函数中的依赖注入失败而发生错误时:

    public class DataController : ApiController
    {
        private readonly IDataService _dataService;

        public DataController(IDataService dataService)
        {
            _dataService = dataService;
        }

        [AllowAnonymous]
        [HttpGet]
        public IHttpActionResult GetSomeStuff()
        {
            return Ok(new AjaxResponse("somestuff"));
        }

none 上述方法捕获错误。我怎样才能捕捉到这些错误?

this blog post 对此进行了很好的描述。摘录回答以下问题:

创建 class:

public class GlobalExceptionHandler : ExceptionHandler
{
    public async override Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
    {
        // Access Exception
        // var exception = context.Exception;

        const string genericErrorMessage = "An unexpected error occured";
        var response = context.Request.CreateResponse(HttpStatusCode.InternalServerError, 
            new
            { 
                Message = genericErrorMessage
            });

        response.Headers.Add("X-Error", genericErrorMessage);
        context.Result = new ResponseMessageResult(response);
    }
}

然后从您的应用程序启动或 owin 设置中如下注册您的异常处理程序:

public static class SetupFiltersExtensions
{
    public static IAppBuilder SetupFilters(this IAppBuilder builder, HttpConfiguration config)
    {
        config.Services.Replace(typeof (IExceptionHandler), new GlobalExceptionHandler());

        return builder;
    }
}

正如他在 post 中所述,他没有使用上述方法登录,而是更喜欢通过 GlobalErrorLogger 登录:

public class GlobalErrorLogger : ExceptionLogger
{
    public override void Log(ExceptionLoggerContext context)
    {
        var exception = context.Exception;
        // Write your custom logging code here
    }
}

注册为:

public static class SetupFiltersExtensions
{
    public static IAppBuilder SetupFilters(this IAppBuilder builder, HttpConfiguration config)
    {
        config.Services.Replace(typeof (IExceptionHandler), new GlobalExceptionHandler());
        config.Services.Add(typeof(IExceptionLogger), new GlobalErrorLogger());

        return builder;
    }
}

我终于自己找到了答案,如下所示。

必须在 GlobalConfiguration.Configuration.Services 级别上实现和覆盖 IHttpControllerActivator 接口(这很重要,因为 config.Services 只处理已经实例化的控制器)。

以下是一些片段:

Startup.cs

    // the following will make sure that any errors that happen within the constructor
    // of any controller due to dependency injection error will also get logged
    GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator),
              new ExceptionHandlingControllerActivator(                        
                 GlobalConfiguration.Configuration.Services.GetHttpControllerActivator())
                    );


ExceptionHandlingControllerActivator.cs

    /// <summary>
    /// This class handles instantiation of every api controller. It handles and logs 
    /// any exception that occurs during instatiation of each controller, e.g. errors
    /// that can happen due to dependency injection.
    /// </summary>
    public class ExceptionHandlingControllerActivator : IHttpControllerActivator
    {
        private IHttpControllerActivator _concreteActivator;
        private static readonly ILog _logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        public ExceptionHandlingControllerActivator(IHttpControllerActivator concreteActivator)
        {
            _concreteActivator = concreteActivator;
        }

        public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
        {
            try
            {
                return _concreteActivator.Create(request, controllerDescriptor, controllerType);
            }
            catch (Exception ex)
            {
                _logger.Error("Internal server error occured while creating API controller " + controllerDescriptor.ControllerName, ex);

                throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Unexpected error while creating controller " + controllerDescriptor.ControllerName));
            }
        }
    }