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中捕获异常并记录下来。