前面讲了webapi的安全验证和参数安全,不清楚的朋友,可以看看前面的文章,《Web API系列(二)接口安全和参数校验》,本文主要介绍Web API异常结果的处理。作为内部或者是对外提供的统一webapi 接口,统一的异常处理,把正确的信息返回给调用者很重要。这样可以让接口开发人员,了解具体的原因所在,这样可以得到有效的错误处理。
需要注意的是,webapi异常的状态码,尽量不要和业务状态码混淆。可以分为两个不同的字段,或者是状态码的规则不同。相关返回数据的格式,可以参考,前面的文章。
1、常规程序异常处理
常规的程序异常,指的是webapi 接口程序在执行的时候出现的各种异常情况,可以使用异常筛选器捕获所有异常。
1. API自定义错误过滤器属性:ApiExceptionAttribute
/// <summary> /// API自定义错误过滤器属性 /// </summary> public class ApiExceptionHandlingAttribute : ExceptionFilterAttribute { /// <summary> /// 统一对调用异常信息进行处理,返回自定义的异常信息 /// </summary> /// <param name="context">HTTP上下文对象</param> public override void OnException(HttpActionExecutedContext context) { //自定义异常的处理 if (context.Exception is NotImplementedException) { throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotImplemented) { //封装处理异常信息,返回指定JSON对象 Content = new StringContent(JsonHelper.ToJson(new ErrorModel((int)HttpStatusCode.NotImplemented, 0, ex.Message)), Encoding.UTF8, "application/json"), ReasonPhrase = "NotImplementedException" }); } else if (context.Exception is TimeoutException) { throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.RequestTimeout) { //封装处理异常信息,返回指定JSON对象 Content = new StringContent(JsonHelper.ToJson(new ErrorModel((int)HttpStatusCode.RequestTimeout, 0, ex.Message)), Encoding.UTF8, "application/json"), ReasonPhrase = "TimeoutException" }); } //.....这里可以根据项目需要返回到客户端特定的状态码。如果找不到相应的异常,统一返回服务端错误500 else { throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError) { //封装处理异常信息,返回指定JSON对象 Content = new StringContent(JsonHelper.ToJson(new ErrorModel((int)HttpStatusCode.InternalServerError, 0, ex.Message)), Encoding.UTF8, "application/json"), ReasonPhrase = "InternalServerErrorException" }); } //base.OnException(context); //记录关键的异常信息 //Debug.WriteLine(context.Exception); } }
2. 定义好了错误过滤器,根据实际情况,在不同级别使用统一的异常处理机制。比如,接口action级别,控制器Controller级别或者是全局。
我们目前的使用的是全局进行异常过滤。在ApiBase 增加异常过滤。
[ApiAuth] [ApiExceptionHandling] public class ApiBase : ApiController
2、地址接口异常处理
对于常规的异常,我们通过上面的处理方式,就可以很好进行拦截并处理了,如果接口异常是全局性的,如访问地不正确,或者调用的接口就不是有效的地址,这样的话,返回的信息就不会被上面的拦截器进行处理了。
如我们给一个无效的API调用路径,在浏览器中获得下面错误结果。
由于上面结果就无法被我们的常规异常拦截器所捕获,因此不会输出经过封装好的异常信息。
所以如果需要拦截,我们需要增加自己的消息代理处理,用来捕获这些特殊的异常信息。
/// <summary> /// API自定义错误消息处理委托类。 /// 用于处理访问不到对应API地址的情况,对错误进行自定义操作。 /// </summary> public class CustomErrorMessageDelegatingHandler : DelegatingHandler { protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { return base.SendAsync(request, cancellationToken).ContinueWith<HttpResponseMessage>((responseToCompleteTask) => { HttpResponseMessage response = responseToCompleteTask.Result; HttpError error = null; if (response.TryGetContentValue<HttpError>(out error)) { //添加自定义错误处理 //error.Message = "Your Customized Error Message"; } if (error != null) { //获取抛出自定义异常,有拦截器统一解析 throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound) { //封装处理异常信息,返回指定JSON对象 Content = new StringContent(JsonHelper.ToJson(new ErrorModel(404, 0, error.Message)), Encoding.UTF8, "application/json"), ReasonPhrase = "Exception" }); } else { return response; } }); } }
同时,在WebApiConfig中,注册上相关处理
public static class WebApiConfig { public static void Register(HttpConfiguration config) { .............. config.MessageHandlers.Add(new CustomErrorMessageDelegatingHandler());
有了以上这两种异常处理,我们就可以统一我们的调用规则,并进行异常记录和显示了,非常方便。
3、总结
首先,以上这两种异常处理,我们就可以统一我们的调用规则,但是对于WebApi里面异常的处理机制,可能还不够深入,但对于一般项目的异常处理基本够用。其他朋友,如果还有什么更好的方案,还望不吝赐教,感谢感谢!
作者:章为忠
关注我的微信公众号,获取相关的 源代码及视频资料。