Web API 異常處理

Web Api的ExceptionFilter只能截獲並處理Action執行過程當中發生的異常,在Action執行過程以外若是出現異常,ExceptionFilter是無能爲力的。服務器

      這些異常包括:框架

  一、  Controller構造方法中出現的異常async

  二、  MessageHandlers中出現的異常ide

  三、  路由過程當中出現的異常ui

  四、  Body在序列化/反序列化過程當中出現的異常url

  由此能夠看出,ExceptionFilter只能解決ApiControler成功實例化後並執行Action期間出現的異常;爲了解決這一個問題,在WEB API中除了ExceptionFilter外還引入了兩個針對異常記錄、處理的擴展點:spa

IExceptionLogger 和IExceptionHandler。日誌

         而這兩個擴展是做爲Web API的管道組件進行註冊管理的,而且,他們有不一樣的分工:code

         IExceptionLogger做爲異常日誌記錄組件,負責異常發生後的日誌記錄,他貫穿於整個Web API的生命週期中,在Web API框架裏,任何一個請求週期中出現任何一個未被捕獲/處理的異常都會首先進入這個異常日誌記錄管道進行異常Log記錄,在Web API中能夠註冊多個IExceptionLogger實例負責不一樣的異常處理。對象

         IExceptionHandler做爲異常處理組件,負責異常發生後的處理工做,他處於異常處理管道的最末端,當IExceptionLogger組件進行一場記錄完畢、沒有相關的ExceptoinFilter進行異常處理時,纔會最終調用ExceptionHandler進行異常處理,在Web API中,有且僅有一個ExceptionHandler進行異常的處理。

         在Web API框架中給出了兩個基類:ExceptionLogger和ExceptionHandler,在使用ExceptionLogger基類時,他提供了ShouldLog虛方法,該方法在基類中被調用,其做用在於避免同一個異常被同一個ExceptionLogger實例重複記錄(如當後續的管道中該異常又被拋出,或者同一個ExceptionLogger對象不當心被註冊了兩次就會出現重複記錄的可能)咱們也能複寫ShouldLog方法加入咱們本身的異常記錄判斷邏輯以針對不一樣的場景進行不一樣的ExceptionLogger調用。若是有興趣能夠反編譯一下ExceptionLogger基類看看,他使用了顯示接口實現,挺有意思的一個技巧。下面咱們來看一個ExceptionLogger使用的例子:

複製代碼
    public class ErroLogger : ExceptionLogger
    {
        public  async Task LogAsync(ExceptionLoggerContext context, CancellationToken cancellationToken)
        {
            var sb = new StringBuilder();
            //獲取Log組件
            ILogger log = LogManager.GetCurrentClassLogger();

            var request = context.Request;

            sb.AppendLine("URL:");
            //獲取URL
            var url = request.RequestUri.ToString();
            sb.AppendLine(url);
           
            log.Error(context.Exception,sb.ToString(),"");
        }

        public override bool ShouldLog(ExceptionLoggerContext context)
        {
            return context.Exception is DemoException && base.ShouldLog(context);
        }
    }
複製代碼

    在這個例子中,咱們重寫了ShouldLog,保證了這個ExceptionLogger只記錄DemoException這個類型的異常,而且也調用了基類方法,保證不會重複記錄同一個異常。在LogAsync方法中,我經過Log組件記錄了致使異常的請求URL,也記錄了異常信息。

接下來咱們要對這個組件進行註冊:

在App_Start/WebApiConfig.cs文件中的Register方法中寫入

config.Services.Add(typeof(IExceptionLogger),new ErroLogger());

    這樣,一個針對DemoException的異常記錄組件就開發完成並註冊完成了,當Web API執行管道中出現未處理的DemoException異常,均會調用則個組件進行記錄。

    接下來咱們來寫一個ExceptionHandler,在整個Web API框架中,ExceptionHandler只能提供一個實例,與ExceptionLogger同樣,咱們能夠繼承ExceptionHandler基類來簡化異常處理,在ExceptionHandler中也提供了ShouldHandle方法來判斷該異常是否應該處理,避免重複處理管道中其餘環節重複拋出的異常。咱們也一樣提供一個例子:

複製代碼
    public class ErrorHandler : ExceptionHandler
    {
        public override async Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
        {
            if (context.Exception is DemoException)
            {
                context.Result = new ResponseMessageResult(context.Request.CreateResponse(HttpStatusCode.BadRequest,new {Message=context.Exception.Message}));
            }
            else
            {
                context.Result = new ResponseMessageResult(context.Request.CreateResponse(HttpStatusCode.InternalServerError,new {Message = "服務器已被外星人綁架"}));
            }
        }
}
複製代碼

    在這個例子中,咱們判斷了異常的類型,並根據不一樣的異常返回客戶端不一樣的響應內容和不一樣的HTTP狀態碼。

    而後在配置中註冊這個異常處理模塊,在App_Start/WebApiConfig.cs文件中的Register方法中寫入

config.Services.Replace(typeof(IExceptionHandler),new ErrorHandler());

    這樣就替換了系統默認的ExceptionHandler,可使用咱們自定義的Handler進行異常的處理了。

    在異常記錄、處理過程當中,咱們都碰到相應的異常上下文參數,咱們能經過這個參數獲取當前請求的上下文,獲取請求、響應(當心有時會爲空哦)、捕獲到該異常的catch塊信息等內容,咱們能夠利用這些信息更好地描述、記錄、處理異常。

    到這裏ExceptionLogger組件和ExceptionHandler組件簡單的開發就完成了。在開發的過程當中咱們能夠看到,ExceptionLogger負責了全局的異常記錄,在Web API框架管道下出現未處理的異常ExceptionLogger都會進行捕獲、記錄。而ExceptionHandler和ExceptionFilter功能是有重疊的,那什麼時候使用ExceptionHandler什麼時候使用ExceptionFilter呢?咱們能夠將二者的區別列出以下的表格:

 

ExceptionFilter

ExceptionHandler

做用域

Controller、Action

全局

實例個數

無限制

全局惟一

做用條件

Controller實例化成功以後

Web API成功加載以後

  通過上面的表咱們能夠看出,若是處理顆粒度細緻到Controller、Action級別時,ExceptionFilter處理起來會更駕輕就熟,他已經能精肯定位到某個Action,而後能夠針對當前Action作定製開發。而ExceptionHandler做用域遠大於ExceptionFilter,他處理全局更有優點。

相關文章
相關標籤/搜索