C#進階系列——WebApi 異常處理解決方案(轉)
出處:http://www.cnblogs.com/landeanfen/p/5363846.html
閱讀目錄
- 一、使用異常篩選器捕獲所有異常
- 二、HttpResponseException自定義異常信息
- 三、返回HttpError
- 四、總結
正文
前言:上篇C#進階系列——WebApi接口傳參不再困惑:傳參詳解介紹了WebApi參數的傳遞,這篇來看看WebApi裏面異常的處理。關於異常處理,作為程序員的我們肯定不陌生,記得在介紹 AOP 的時候,我們講過通過AOP可以統一截獲異常。那麽在我們的WebApi裏面一般是怎麽處理異常的呢,今天這一篇,博主帶著大家一起來實踐下WebApi的異常處理。
WebApi系列文章
- C#進階系列——WebApi接口測試工具:WebApiTestClient
- C#進階系列——WebApi 跨域問題解決方案:CORS
- C#進階系列——WebApi身份認證解決方案:Basic基礎認證
- C#進階系列——WebApi接口傳參不再困惑:傳參詳解
- C#進階系列——WebApi接口返回值不困惑:返回值類型詳解
- C#進階系列——WebApi異常處理解決方案
- C#進階系列——WebApi區域Area使用小結
為什麽說是實踐?因為在http://www.asp.net裏面已經明確給出WebApi的異常處理機制。光有理論還不夠,今天我們還是來試一把。通過實踐,我們可能發現一些更詳盡的用法。
回到頂部一、使用異常篩選器捕獲所有異常
我們知道,一般情況下,WebApi作為服務使用,每次客戶端發送http請求到我們的WebApi服務裏面,服務端得到結果輸出response到客戶端。這個過程中,一旦服務端發生異常,會統一向客戶端返回500的錯誤。
[HttpGet] public string GetAllChargingData([FromUri]TB_CHARGING obj) { throw new NotImplementedException("方法不被支持"); }
我們來看看http請求
而有些時候,我們客戶端需要得到更加精確的錯誤碼來判斷異常類型,怎麽辦呢?
記得在介紹AOP的時候,我們介紹過MVC裏面的IExceptionFilter接口,這個接口用於定義異常篩選器所需的方法,在WebApi裏面,也有這麽一個異常篩選器,下面我們通過一個實例來看看具體如何實現。
首先在App_Start裏面新建一個類WebApiExceptionFilterAttribute.cs,繼承ExceptionFilterAttribute,重寫OnException方法
按 Ctrl+C 復制代碼 按 Ctrl+C 復制代碼代碼解析:通過判斷異常的具體類型,向客戶端返回不同的http狀態碼,示例裏面寫了兩個,可以根據項目的實際情況加一些特定的我們想要捕獲的異常,然後將對應的狀態碼寫入http請求的response裏面,對於一些我們無法判斷類型的異常,統一返回服務端錯誤500。關於http的狀態碼,framework裏面定義了一些常見的類型,我們大概看看:
Http狀態碼定義好了異常處理方法,剩下的就是如何使用了。可以根據實際情況,在不同級別使用統一的異常處理機制。
1、接口級別
[WebApiExceptionFilter] [HttpGet] public string GetAllChargingData([FromUri]TB_CHARGING obj) { throw new NotImplementedException("方法不被支持"); }
執行到異常後,會先進到OnException方法:
執行完成之後瀏覽器查看:
如果需要,甚至可以向Status Code裏面寫入自定義的描述信息,並且還可以向我們的Response的Content裏面寫入我們想要的信息。我們稍微改下OnException方法:
if (actionExecutedContext.Exception is NotImplementedException) { var oResponse = new HttpResponseMessage(HttpStatusCode.NotImplemented); oResponse.Content = new StringContent("方法不被支持"); oResponse.ReasonPhrase = "This Func is Not Supported"; actionExecutedContext.Response = oResponse; }
看看ReasonPhrase描述信息
看看Response的描述信息
2、控制器級別
如果想要某一個或者多個控制器裏面的所有接口都使用異常過濾,直接在控制器上面標註特性即可。
- 某一個控制器上面啟用異常過濾
[WebApiExceptionFilter] public class ChargingController : BaseApiController { #region Get [HttpGet] public string GetAllChargingData([FromUri]TB_CHARGING obj) { throw new NotImplementedException("方法不被支持"); } }
- 多個控制器上面同時啟用異常過濾
[WebApiExceptionFilter] public class BaseApiController : ApiController { }
public class ChargingController : BaseApiController { #region Get [HttpGet] public string GetAllChargingData([FromUri]TB_CHARGING obj) { throw new NotImplementedException("方法不被支持"); } }
這樣,所有繼承BaseApiController的子類都會啟用異常過濾。
3、全局配置
如果需要對整個應用程序都啟用異常過濾,則需要做如下兩步:
1、在Global.asax全局配置裏面添加 GlobalConfiguration.Configuration.Filters.Add(new WebApiExceptionFilterAttribute()); 這一句,如下:
void Application_Start(object sender, EventArgs e) { // 在應用程序啟動時運行的代碼 AreaRegistration.RegisterAllAreas(); GlobalConfiguration.Configure(WebApiConfig.Register); RouteConfig.RegisterRoutes(RouteTable.Routes); GlobalConfiguration.Configuration.Filters.Add(new WebApiExceptionFilterAttribute()); }
2、在WebApiConfig.cs文件的Register方法裏面添加 config.Filters.Add(new WebApiExceptionFilterAttribute()); 這一句,如下:
public static void Register(HttpConfiguration config) { //跨域配置 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); // Web API 路由 config.MapHttpAttributeRoutes(); RouteTable.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional } ).RouteHandler = new SessionControllerRouteHandler(); config.Filters.Add(new WebApiExceptionFilterAttribute()); }回到頂部
二、HttpResponseException自定義異常信息
上面說的是全局的異常捕獲以及處理方式,在某些情況下,我們希望以異常的方式向客戶端發送相關信息,可能就需要用到我們的HttpResponseException。比如:
[HttpGet] public TB_CHARGING GetById(string id) { //從後臺查詢實體 var oModel = server.Find(id); if (oModel == null) { var resp = new HttpResponseMessage(HttpStatusCode.NotFound) { Content = new StringContent(string.Format("沒有找到id={0}的對象", id)), ReasonPhrase = "object is not found" }; throw new HttpResponseException(resp); } return oModel; }
執行之後瀏覽器裏面查看結果:
代碼釋疑:細心的朋友可能,發現了,這裏既使用了HttpResponseMessage,又使用了HttpResponseException,那麽,像這種可控的異常,我們是否可以直接以HttpResponseMessage的形式直接返回到客戶端而不用拋出異常呢?這裏就要談談這兩個對象的區別了,博主的理解是HttpResonseMessage對象用來響應訊息並包含狀態碼及數據內容,HttpResponseException對象用來向客戶端返回包含錯誤訊息的異常。
在網上看到一篇 文章 這樣描述兩者的區別:當呼叫 Web API 服務時發生了與預期上不同的錯誤時,理當應該中止程序返回錯誤訊息,這時對於錯誤的返回就該使用 HttpResponseException,而使用 HttpResponseMessage 則是代表著當客戶端發送了一個工作請求而 Web API 正確的完成了這個工作,就能夠使用 HttpResponseMessage 返回一個 201 的訊息,所以 HttpResponseMessage 與 HttpResponseException 在使用上根本的目標就是不同的,用 HttpResponseMessage 去返回一個例外錯誤也會讓程序結構難以辨別且不夠清晰。
回到頂部三、返回HttpError
HttpError對象提供一致的方法來響應正文中返回錯誤的信息。準確來說,HttpError並不是一個異常,只是用來包裝錯誤信息的一個對象。其實在某一定的程度上,HttpError和HttpResponseMessage使用比較相似,二者都可以向客戶端返回http狀態碼和錯誤訊息,並且都可以包含在HttpResponseException對象中發回到客戶端。但是,一般情況下,HttpError只有在向客戶端返回錯誤訊息的時候才會使用,而HttpResponseMessage對象既可以返回錯誤訊息,也可返回請求正確的消息。其實關於HttpError沒什麽特別好講的,我們來看一個例子就能明白:
public HttpResponseMessage Update(dynamic obj) { TB_Product oModel = null; try { var id = Convert.ToString(obj.id); oModel = Newtonsoft.Json.JsonConvert.DeserializeObject<TB_Product>(Convert.ToString(obj.dataModel)); //...復雜的業務邏輯 } catch(Exception ex) { return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex.Message); } return Request.CreateResponse<TB_Product>(HttpStatusCode.OK, oModel); }
假如現在在執行try裏面復雜業務邏輯的時候發生了異常,我們捕獲到了異常然後向客戶端返回HttpError對象,這個對象裏面包含我們自定義的錯誤訊息,如果正常則返回HttpResponseMessage對象。
如果請求異常:
如果請求正常
回到頂部四、總結
以上三種異常的處理方法,可以根據不同的場景選擇使用。
- 如果項目對異常處理要求並不高,只需要記錄好異常日誌即可,那麽使用異常篩選器就能夠搞定
- 如果項目需要對不同的異常,客戶端做不同的處理。而這個時候使用異常篩選器不能詳盡所有的異常,可能使用HttpResponseException對象是更好的選擇,定義更加精細的異常和異常描述。
- 對於何時使用HttpError,又何時使用HttpResponseMessage,可以參考上文三裏面用法。
- 當然實際項目中很可能以上兩種或者三種同時使用。
上文通過一些簡單的示例介紹了下WebApi裏面異常的處理機制,可能不夠深入,但對於一般項目的異常處理基本夠用。其實有一點博主還沒有想明白,對於構造函數裏面的異常該如何統一捕獲呢?通過異常篩選器是捕獲不到的,不知道園友們有沒有什麽更好的辦法,不吝賜教,感謝感謝!如果本文能幫到你,不妨推薦下,您的推薦是博主繼續總結的動力!
C#進階系列——WebApi 異常處理解決方案(轉)