1. 程式人生 > >C#進階系列——WebApi 異常處理解決方案(轉)

C#進階系列——WebApi 異常處理解決方案(轉)

機制 輸出 ges 如果 但是 rom lba slist 解決

出處: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 異常處理解決方案(轉)