【问题标题】:Catching dependency injection errors in controllers in .NET Web Api在 .NET Web Api 的控制器中捕获依赖注入错误
【发布时间】:2020-03-02 04:49:36
【问题描述】:

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

控制器致命的服务器错误处理/记录通常可以通过以下方式很好地描述:

  1. 在 Global.asax.cs 中实现/覆盖 Application_Error 方法
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"));
        }

以上方法都没有捕捉到错误。我怎样才能发现这些错误?

【问题讨论】:

    标签: c# .net asp.net-mvc-4 asp.net-web-api


    【解决方案1】:

    这在this blog post 中有很好的描述。摘录以下问题:

    创建一个类:

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

    正如他的帖子中所述,他没有使用上述方法登录,而是更喜欢通过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;
        }
    }
    

    【讨论】:

    • 这实际上并不能解决每个控制器实例化期间发生的错误。它只处理控制器方法中发生的错误。
    • GlobalExceptionFilter 不会像您的问题一样在实例化期间捕获。 GlobalExceptionHandler 应该如帖子中所述。我已经在one 这样的项目中成功使用了这种技术
    【解决方案2】:

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

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

    这里有一些sn-ps:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-05
      • 2016-02-01
      • 1970-01-01
      • 2015-12-10
      相关资源
      最近更新 更多