在上篇隨筆《Web API應用架構在Winform混合框架中的應用(1)》中我介紹了關於如何在Winfrom里面整合WebAPI,作為一個新型數據源的接入方式,從而形成了三種不同的數據提供來源,前文在介紹整體性框架方面,着筆較多,注重整合的統一性,但是在Web API方面還不算很具體,本系列將繼續這個主題,介紹Web API開發中常見到的一些問題,對其中各個技術要點進行總結,本文主要介紹Web API自定義異常結果的處理。
1、常規的異常處理
統一的異常處理,把正確的信息返回給調用者很重要,可以讓接口開發人員或者用戶,了解具體的原因所在,這樣可以得到有效的錯誤處理。
參考微信API的處理,微信API,對於調用都有一個錯誤信息返回,不會直接裸露未經處理的異常,因此它們都是經過了一定的攔截處理,然后把錯誤信息包裝提供給接口調用方的。如下是微信的一些接口處理錯誤。
錯誤時微信會返回錯誤碼等信息,JSON數據包示例如下(該示例為AppID無效錯誤):
{"errcode":40013,"errmsg":"invalid appid"}
我們根據自己的需要,定義了一個統一的錯誤信息實體類,如下所示。
/// <summary> /// 接口返回的錯誤信息 /// </summary> public class BaseResultJson { /// <summary> /// 錯誤代碼 /// </summary> public int errcode { get; set; } /// <summary> /// 如果不成功,返回的錯誤信息 /// </summary> public string errmsg { get; set; } /// <summary> /// 是否成功 /// </summary> public bool success { get; set; } }
這樣我們就可以把攔截到的錯誤信息,轉換為這樣一個方便使用的實體類信息了。
攔截Web API的調用異常,一般可以結合Try Catch的方法,以及異常攔截器進行處理,如下是主動拋出的一些異常信息處理。
//如果沒有通過,則拋出異常,由異常過濾器統一處理 if (!result.success) { throw new MyApiException(result.errmsg, result.errcode); }
其中MyApiException是自定義的一個異常信息,用來承載自定義錯誤信息的異常類。
異常攔截器,我們在Web API里面可以通過Attribute這種標簽特性進行處理,如下是我在Web API的基類里面定義了一個異常處理器。
/// <summary> /// 所有接口基類 /// </summary> [ExceptionHandling] public class BaseApiController : ApiController
這個特性對象的定義,它的代碼如下所示。
/// <summary> /// API自定義錯誤過濾器屬性 /// </summary> public class ExceptionHandlingAttribute : ExceptionFilterAttribute { /// <summary> /// 統一對調用異常信息進行處理,返回自定義的異常信息 /// </summary> /// <param name="context">HTTP上下文對象</param> public override void OnException(HttpActionExecutedContext context) { //自定義異常的處理 MyApiException ex = context.Exception as MyApiException; if (ex != null) { throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError) { //封裝處理異常信息,返回指定JSON對象 Content = new StringContent(new BaseResultJson(ex.Message, false, ex.errcode).ToJson()), ReasonPhrase = "Exception" }); } //記錄關鍵的異常信息 Debug.WriteLine(context.Exception); //常規異常的處理 string msg = string.IsNullOrEmpty(context.Exception.Message) ? "接口出現了錯誤,請重試或者聯系管理員" : context.Exception.Message; throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError) { Content = new StringContent(msg), ReasonPhrase = "Critical Exception" }); } }
根據這些代碼,我們就可以實現對調用異常的統一封裝處理,讓它給我們返回統一的對象信息了,如下是其中一個調用異常,轉換為自定義異常信息后的結果輸出。
{"errcode":404,"errmsg":"請求的資源不支持 http 方法“POST”。","success":false}
這樣我們在處理Web API的返回結果的時候,可以先處理它的異常信息,具體的處理代碼如下所示。
HttpHelper helper = new HttpHelper(); helper.ContentType = "application/json"; string content = helper.GetHtml(url, postData, true); VerifyErrorCode(content); T result = JsonConvert.DeserializeObject<T>(content); return result;
我們在上面紅色部分的代碼就是先處理異常定義信息,如果有這些異常,我們可以在界面中進行異常處理顯示了。
例如,如果自定義異常存在,我們轉換后,把對應的信息顯示出來,重新拋出異常即可。
BaseResultJson errorResult = JsonConvert.DeserializeObject<BaseResultJson>(content); //非成功操作才記錄異常,因為有些操作是返回正常的結果({"errcode": 0, "errmsg": "ok"}) if (errorResult != null && !errorResult.success) { string error = string.Format("請求發生錯誤!錯誤代碼:{0},說明:{1}", (int)errorResult.errcode, errorResult.errmsg); LogTextHelper.Error(errorResult.ToJson()); throw new Exception(error);//拋出錯誤 }
2、地址接口異常處理
對於常規的異常,我們通過上面的處理方式,就可以很好進行攔截並處理了,如果接口異常是全局性的,如訪問地址簿正確,或者參數多了幾個信息,那么調用的接口就不是有效的地址,這樣的話,返回的信息就不會被上面的攔截器進行處理了。
如我們給一個無效的API調用路徑,在瀏覽器中獲得下面錯誤結果。
上面結果就無法被我們的常規異常攔截器所捕獲,因此不會輸出經過封裝好的異常信息。
如果需要攔截,我們需要增加自己的消息代理處理,用來捕獲這些特殊的異常信息。
public static class WebApiConfig { public static void Register(HttpConfiguration config) { .............. config.MessageHandlers.Add(new CustomErrorMessageDelegatingHandler());
上面紅色部分就是我們自己添加的消息代理處理類,用來處理一些特殊的異常信息,如下代碼所示。
/// <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(new BaseResultJson(error.Message, false, 404).ToJson()), ReasonPhrase = "Exception" }); } else { return response; } }); } }
經過了上面的處理后,我們進一步測試一下不存在的地址的異常處理結果,可以看到輸出的內容是經過了自定義對象的轉換了。
常規的調用,如果接口不對應,那么錯誤也是類似下面的消息
{"errcode":404,"errmsg":"找不到與請求 URI“http://localhost:9001/api/SystemType/Delete?signature=72f8d706c79dc14d70fc3f080d4706748d754021×tamp=1443194061&nonce=0.650359861855563&appid=website_9A39C2A8&token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIxIiwiaWF0IjoxNDQzMTk0MDM4LCJqdGkiOiI1YmEyYmE5Ni0yZTA4LTQ1ZTgtYTAwNy01MmY3OTkzYTg2NzEiLCJuYW1lIjoiYWRtaW4iLCJjaGFubmVsIjoiMCIsInNoYXJlZGtleSI6IjEyMzRhYmNkIn0.RRXQmmSCJzDK5Or6rmBL5wjd-YIJoEQFc0pOzqhR6IU”匹配的 HTTP 資源。","success":false}
有了這些信息,我們就可以統一我們的調用規則,並進行異常記錄和顯示了,非常方便。
系列文章如下所示:
Web API應用架構在Winform混合框架中的應用(1)
Web API應用架構在Winform混合框架中的應用(2)--自定義異常結果的處理