asp.net web api 异常捕获

1 向客户端发送错误消息

使用throw new HttpResponseException()向客户端抛出错误信息。

HttpResponseException包含两个重载的构造函数,其中一个是构造函数参数类型为HttpResponseMessage,通过其设置状态码,错误消息短语以及消息体内容来向客户端抛出比较详细的错误信息。另一个参数类型为HttpStatusCode,只能设定状态码。

2自定义异常过滤器

扩展IExceptionFilter来定义异常过滤器。异常过滤器不会捕获类型为HttpResponseException的异常,下面的异常也无法被异常过滤器捕获:

1)controller构造器抛出的异常

2)消息处理器抛出的异常

3)路由过程中抛出的异常

4)响应内容序列化与反序列化过程中抛出的异常

代码示例:

public class CustomExceptionFilterAttribute : ExceptionFilterAttribute 
    {
        public override void OnException(HttpActionExecutedContext context)
        {
            if (context.Exception!=null)
            {
                LogHelper.LogError(context.Exception);
            }
        }
    }

3 扩展ExceptionHandler和ExceptionLogger

扩展ExceptionHandler可以捕获大部分异常,包括一些无法被异常过滤器捕获的异常。但是HttpResponseException类型的异常不会被捕获。

示例代码:

/// <summary>
    /// 自定义的异常处理程序
    /// </summary>
    public class GlobalExceptionHandler : ExceptionHandler
    {
        /// <summary>
        /// 处理异常
        /// </summary>
        /// <param name="context"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public override Task HandleAsync(ExceptionHandlerContext context,CancellationToken cancellationToken)
        {
            if (!ShouldHandle(context))
            {
                return Task.FromResult(0);
            }
            context.Result = new ErrorResult
            {
                Request = context.ExceptionContext.Request,
                Content = "呀! 有异常,请联系管理员"
            };
            return Task.FromResult(0);
        }
        /// <summary>
        /// 判断是否应该处理
        /// 后期扩展,重写方法可过滤掉不需处理的异常
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override bool ShouldHandle(ExceptionHandlerContext context)
        {
            return true;
        }
        private class ErrorResult : IHttpActionResult
        {
            public HttpRequestMessage Request { get; set; }
            public string Content { get; set; }
            public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
            {
                HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.InternalServerError);
                response.Content = new StringContent(Content);
                response.RequestMessage = Request;
                return Task.FromResult(response);
            }
        }
    }
public class GlobalExceptionLogger : ExceptionLogger
    {
        public override Task LogAsync(ExceptionLoggerContext context,CancellationToken cancellationToken)
        {
            if (!ShouldLog(context))
            {
                return Task.FromResult(0);
            }
            if (context.Exception != null)
            {
                string msg = ClientInfoAnalysis.GetClientInfo();
                LogHelper.LogError(context.Exception, msg);
            }
            return Task.FromResult(0);
        }
        /// <summary>
        /// 判断是否应记录异常
        /// 后期重写此方法,可过滤掉不需要记录的异常信息
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override bool ShouldLog(ExceptionLoggerContext context)
        {
            if ((context.Exception is System.Web.HttpException))
            {
                return false;
            }
            return true;
        }
}
public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // 加载log4net配置文件
            LogConfigLoading.Load(AppSettings.Log4netPathForWeb);

            // 加载Web API服务
            config.Services.Replace(typeof(IAssembliesResolver), new ServiceAssembliesResolver(AppSettings.ServicesLocation));

            // 全局异常信息处理
            config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());

            // 全局异常记录
            config.Services.Add(typeof(IExceptionLogger), new GlobalExceptionLogger());
}
}

4某些异常无法被捕获的异常

问题描述

对于在服务加载过程中的异常,无法通过异常过滤器,即实现了System.Web.Http.Filters.IExceptionFilter接口的过滤器来捕获,也不能通过注册ExceptionLogger来达到目的。解决方法如下:

public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            try
            {
                config.Services.Replace(typeof(IAssembliesResolver), new ServiceAssembliesResolver(SysSettings.ServicesLocation));
            }
            catch (Exception ex)
            {
                LogHelper.Error(ex);
            }

//其他代码
}
}

其中ServiceAssembliesResolver为:

public class ServiceAssembliesResolver : DefaultAssembliesResolver
    {
        //服务插件路径
        private string path;
        public ServiceAssembliesResolver(string path):base()
        {
            this.path = path;
        }
        public override ICollection<Assembly> GetAssemblies()
        {
            //获得已有的服务
            ICollection<Assembly> baseAssemblies = base.GetAssemblies();
            //初始化
            List<Assembly> assemblies = new List<Assembly>(baseAssemblies);
            //加载每一个服务插件
            foreach (string file in Directory.GetFiles(path, "*.dll"))
            {
                var controllersAssembly = Assembly.LoadFrom(file);
                assemblies.Add(controllersAssembly);
            }

            return assemblies;
        }
    }

但上述方法很可能不起作用,根本原因在于将config.Services.Replace(typeof(IAssembliesResolver), new ServiceAssembliesResolver(SysSettings.ServicesLocation));放入try-catch块中,若ServiceAssembliesResolver在实例化的时候不抛出异常,而是当调用GetAssemblies时抛出异常(例如服务插件存储文件夹被删除),此时无法记录异常。那么问题就在于GetAssemblies方法何时被调用,通过跟踪代码发现Register中的所有代码都执行完成才会加载服务。解决办法是在ServiceAssembliesResolver.GetAssemblies中捕获异常并记录下来。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大内老A

ASP.NET MVC中的ActionFilter是如何执行的?

在ASP.NET MVC中的四大筛选器(Filter),ActionFilter直接应用在某个Action方法上,它在目标Action方法执行前后对调用进行拦截...

1907
来自专栏java学习

关于Spring 和 Spring MVC的43个问题【问题汇总】

通过Spring提供的IoC容器,可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。

541
来自专栏IT可乐

Spring详解(三)------DI依赖注入

  上一篇博客我们主要讲解了IOC控制反转,也就是说IOC 让程序员不在关注怎么去创建对象,而是关注与对象创建之后的操作,把对象的创建、初始化、销毁等工作交给s...

1825
来自专栏大内老A

总体介绍ASP.NET Web API下Controller的激活与释放流程

通过《ASP.NET Web API的Controller是如何被创建的?》我们已经对HttpController激活系统的核心对象有了深刻的了解,这些对象包括...

1687
来自专栏大内老A

ASP.NET MVC Controller激活系统详解:总体设计

我们将整个ASP.NET MVC框架划分为若干个子系统,那么针对请求上下文激活目标Controller对象的子系统被我们成为Controller激活系统。在正式...

1946
来自专栏古时的风筝

ASP.NET-自定义HttpModule与HttpHandler

在之前的ASP.NET是如何在IIS下工作的这篇文章中介绍了ASP.NET与IIS配合工作的机制,在http请求经过一系列处理后,最后到达ASP.NET管道中...

2348
来自专栏Spring相关

第7章—SpringMVC高级技术—处理multipart形式的数据

MultipartResolver 用于处理文件上传,当收到请求时 DispatcherServlet 的 checkMultipart() 方法会调用 Mul...

1212
来自专栏大内老A

ASP.NET的路由系统:根据路由规则生成URL

前面我们已经提到过,ASP.NET 的路由系统主要具有两个方面的应用,其一就是通过注册URL模板与物理文件路径的匹配实现请求地址和物理地址的分离;另一个则是通过...

1908
来自专栏大内老A

ASP.NET Web API路由系统:路由系统的几个核心类型

虽然ASP.NET Web API框架采用与ASP.NET MVC框架类似的管道式设计,但是ASP.NET Web API管道的核心部分(定义在程序集Syste...

26110
来自专栏前端架构与工程

JavaScript实现私有属性

JavaScript被很多人认为并不是一种面向对象语言,原因有很多种,比如JavaScript没有类,不能提供传统的类式继承;再比如JavaScript不能实现...

1675

扫码关注云+社区