1. 程式人生 > >C#進階--WebApi異常處理機制

C#進階--WebApi異常處理機制

str with 分配 客戶 來看 stack 統一 releases 全局配置

其實對於C#異常處理大家都不陌生,但是對於在WeiApi上的異常處理實際上也和傳統異常處理區別不大,但是卻經過封裝可以讓異常更加友好,https://docs.microsoft.com/en-us/aspnet/web-api/overview/error-handling/exception-handling,通過微軟的官方介紹,我們可以知道WeiApi可以簡單概述為三種異常,接下來我們圍繞這三種異常給出例子,如何封裝和處理以上三種異常

異常過濾

異常過濾器實現了System.Web.Http.Filters.IExceptionFilter

接口。編寫異常過濾器最簡單的方法是從System.Web.Http.Filters.ExceptionFilterAttribute派生並重寫OnException方法。Microsoft給出的關於異常過濾器解釋,那麽如何實現呢?通過閱讀《Asp.NET WEB API2 框架揭秘》,我們知道每一次客戶端請求API都會通過HTTP請求,服務端得到結果輸出response到客戶端。這個過程中,一旦服務端發生異常,會統一向客戶端返回500的錯誤。

那麽在Web API中如何定義NotImplementedException類?首先在App_Start裏面新建一個類WebApiExceptionFilterAttribute.cs,繼承ExceptionFilterAttribute,重寫OnException方法,代碼如下

技術分享圖片
 1 public class WebApiExceptionFilterAttribute : ExceptionFilterAttribute 
 2     {
 3         //重寫基類的異常處理方法
 4         public override void OnException(HttpActionExecutedContext actionExecutedContext)
 5         {
 6             //1.異常日誌記錄(正式項目裏面一般是用log4net記錄異常日誌)
 7             Console.WriteLine(DateTime.Now.ToString("
yyyy-MM-dd HH:mm:ss") + "——" + 8 actionExecutedContext.Exception.GetType().ToString() + "" + actionExecutedContext.Exception.Message + "——堆棧信息:" + 9 actionExecutedContext.Exception.StackTrace); 10 11 //2.返回調用方具體的異常信息 12 if (actionExecutedContext.Exception is NotImplementedException) 13 { 14 actionExecutedContext.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented); 15 } 16 else if (actionExecutedContext.Exception is TimeoutException) 17 { 18 actionExecutedContext.Response = new HttpResponseMessage(HttpStatusCode.RequestTimeout); 19 } 20 //.....這裏可以根據項目需要返回到客戶端特定的狀態碼。如果找不到相應的異常,統一返回服務端錯誤500 21 else 22 { 23 actionExecutedContext.Response = new HttpResponseMessage(HttpStatusCode.InternalServerError); 24 } 25 26 base.OnException(actionExecutedContext); 27 } 28 }
View Code

代碼解析:通過判斷異常的具體類型,向客戶端返回不同的http狀態碼,示例裏面寫了兩個,可以根據項目的實際情況加一些特定的我們想要捕獲的異常,然後將對應的狀態碼寫入http請求的response裏面,對於一些我們無法判斷類型的異常,統一返回服務端錯誤500。Microsoft也有一個代碼實現,但是沒有封裝

註冊異常過濾

有幾種方法可以註冊Web API異常過濾器:

  • 接口級別
  • 控制器級別
  • 全局配置

要將過濾器應用於特定操作,請將過濾器作為屬性添加到操作中:

public class ProductsController : ApiController
{
    [NotImplExceptionFilter]
    public Contact GetContact(int id)
    {
        throw new NotImplementedException("This method is not implemented");
    }
}

  要將過濾器應用於控制器上的所有操作,請將過濾器作為屬性添加到控制器類中:

[NotImplExceptionFilter]
public class ProductsController : ApiController
{
    // ...
}

  要將過濾器全局應用於所有Web API控制器,請將過濾器實例添加到GlobalConfiguration.Configuration.Filters集合中。此集合中的執行篩選器適用於任何Web API控制器操作。

GlobalConfiguration.Configuration.Filters.Add(
    new ProductStore.NotImplExceptionFilterAttribute());

  如果需要,甚至可以向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;
            }

HttpResponseException自定義異常信息

異常過濾器是針對接口控制器以及全局定義,一般返回的都是服務器級別的錯誤,但是有的時候我們需要定義業務異常的代碼,那麽如果是業務異常的情況下我們就可以使用HttpResponseException自定義異常,如下述代碼:

public Product GetProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
        {
            Content = new StringContent(string.Format("沒有找到產品ID = {0}的產品", id)),
            ReasonPhrase = "Product ID Not Found"
        }
        throw new HttpResponseException(resp);
    }
    return item;
}

可以看到具體的業務異常信息是通過HttpResponseMessage封裝,最後由HttpResponseException拋出,Microsoft沒有解釋HttpResponseMessage是什麽,起初筆者以為這是一個HttpResponseException的子類,但是跟蹤了後發現HttpResponseMessage 繼承了IDisposable接口,而IDisposable接口的主要用途是釋放非托管的資源。 垃圾回收器自動釋放不再使用該對象時分配給托管對象的內存。 但是,不可能預測將發生垃圾回收。 此外,垃圾回收器具有不知道如窗口句柄的非托管資源,或打開文件和流。從描述上來看使用HttpResponseMessage時就代表發生了異常並且進行一次資源管理的回收,所以筆者認為,使用HttpResponseMessage可以更清晰的描述業務中所發生的 異常,並且在返回異常的時候進行一次垃圾回收,減少程序資源浪費

HttpError

HttpError對象提供了一個一致的方式在回應主體中返回的錯誤信息。以下示例顯示如何在響應正文中使用HttpError返回HTTP狀態碼404(Not Found)。通過Microsoft的解釋就可以知道HttpError提供的是狀態碼返回,那麽實際應用上更多的是將其和HttpResponseException一起使用。

public Product GetProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        var message = string.Format("Product with id = {0} not found", id);
        throw new HttpResponseException(
            Request.CreateErrorResponse(HttpStatusCode.NotFound, message));
    }
    else
    {
        return item;
    }
}

總結

借鑒C#進階系列——WebApi 異常處理解決方案,

借鑒(Microsoft官網)ASP.NET Web API中的異常處理

在一般的項目中,可以定義好一些全體的關於服務器端的異常處理,並且封裝好一個HttpResponseException自定義異常的處理,無需捕獲HttpResponseException異常,Api會自己處理這個異常的,並且最好為每個異常給出更加詳細的HTTP狀態碼,可以讓異常更加精細友好

C#進階--WebApi異常處理機制