自定義異常過濾特性

在ASP.NET MVC中,默認的異常處理機制有時候沒法知足項目的業務需求,咱們能夠經過實現IExceptionFilter接口編寫本身想要的異常處理代碼,好比全局異常捕獲,記錄錯誤日誌等自定義異常處理操做。html

文章演示代碼的下載地址:GlobalExceptionHandle-By-IExceptionFilter ,項目使用的是VS2013開發工具,ASP.NET MVC 5框架,建議下載查看演示,有更加詳細的代碼和直觀的界面。web

ASP.NET MVC默認提供了HandleError特性做爲全局異常處理的機制,可是有部分的侷限性,好比不捕捉404錯誤,依賴ASP.NET的自定義錯誤模塊,對SEO不友好(由於錯誤頁面跳轉的方式爲302)等等,因此若是想在項目中對異常錯誤進行更多自定義操做,能夠繼承並重寫HandleError特性,或者使用IExceptionFilter編寫本身的異常處理特性。編程

PS:HandleError也是經過實現IExceptionFilter接口來進行相關操做,具體能夠查看HandleErrorAttribute源碼。若是沒用到HandleError特性的一些屬性,仍是推薦使用IExceptionFilter接口,這樣代碼會比較精煉,而且能夠更自由的進行功能擴展和代碼編寫。swift

自定義異常過濾器的關鍵代碼

這裏先新建一個ASP.NET MVC項目,接着新建一個類庫,之後全部自定義特性和過濾器的類都放在這個類庫下,這裏我將類庫命名爲CustomAttributeClassLibrary。服務器

接下來在類庫中添加要編寫自定義異常處理的類,這裏我命名爲CustomExceptionAttribute。mvc

CustomExceptionAttribute類代碼以下:框架

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web; using System.Web.Mvc; namespace CustomAttributeClassLibrary { public class CustomExceptionAttribute : FilterAttribute, IExceptionFilter { public void OnException(ExceptionContext filterContext) { Exception exception = filterContext.Exception; if (filterContext.ExceptionHandled == true) { return; } HttpException httpException = new HttpException(null, exception); //filterContext.Exception.Message可獲取錯誤信息 /* * 一、根據對應的HTTP錯誤碼跳轉到錯誤頁面 * 二、先對Action方法裏引起的HTTP 404/400錯誤進行捕捉和處理 * 三、其餘錯誤默認爲HTTP 500服務器錯誤 */ if (httpException != null && (httpException.GetHttpCode() == 400 || httpException.GetHttpCode() == 404)) { filterContext.HttpContext.Response.StatusCode = 404; filterContext.HttpContext.Response.WriteFile("~/HttpError/404.html"); } else { filterContext.HttpContext.Response.StatusCode = 500; filterContext.HttpContext.Response.WriteFile("~/HttpError/500.html"); } /*--------------------------------------------------------- * 這裏可進行相關自定義業務處理,好比日誌記錄等 ---------------------------------------------------------*/ //設置異常已經處理,不然會被其餘異常過濾器覆蓋 filterContext.ExceptionHandled = true; //在派生類中重寫時,獲取或設置一個值,該值指定是否禁用IIS自定義錯誤。 filterContext.HttpContext.Response.TrySkipIisCustomErrors = true; } } }

上面代碼中主要實現了一個IExceptionFilter接口和一個FilterAttribute基類。這裏先說IExceptionFilter接口,接口只提供了一個方法OnException,主要的參數爲ExceptionContext類,基本上就是經過ExceptionContext類來獲取相關錯誤信息,以及進行對應的HTTP請求和響應操做。工具

ExceptionContext類文檔的截圖說明:post

ExceptionContext 類屬性說明

至於FilterAttribute抽象類,若是打算將編寫的異常過濾器做爲特性來使用,那麼它必須繼承自FilterAttribute或者它的子類HandleErrorAttribute。不做爲特性使用的全局操做過濾器能夠不繼承這個基類。學習

下圖演示能夠看到沒有繼承FilterAttribute類就沒法做爲特性來使用:

未繼承FilterAttribute抽象類的過濾器沒法做爲特性使用

可是不影響全局操做過濾器的註冊和使用:

註冊自定義全局異常過濾器

記得使用NUGET將ASP.NET MVC 5 安裝到新類庫中,IExceptionFilter是屬於System.Web.Mvc程序集,並且編寫自定義的過濾器和特性會使用到不少與System.Web.Mvc命名空間有關的類。

自定義異常特性類庫須要添加ASP.NET MVC命名空間

到這裏就完成了整個自定義異常處理過濾器的功能。

在項目中使用自定義異常過濾器

有兩種使用方式,一種是經過全局過濾器類GlobalFilterCollection進行註冊,還有一種是單獨的做爲特性在控制器或操做方法中使用。

咱們先將自定義特性類庫引用到默認的MVC項目中:

項目中引入自定義特性類庫

全局註冊的使用只須要在FilterConfig過濾器配置類中註冊下便可,代碼以下:

public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute(), 1); filters.Add(new CustomExceptionAttribute(), 2); } }

PS:關於FilterConfig類,若是不清楚的朋友能夠看這篇文章:ASP.NET MVC 5 學習筆記之FilterConfig類

這裏要注意篩選器的運行順序設置,引用文檔資料

在 ASP.NET MVC 版本 1 和 2 中,OnException(ExceptionContext) 篩選器以正向順序運行。 在 ASP.NET MVC 版本 3 及更高版本中,此順序已反轉。

因此Order=2的優先級反而比較高,註冊好後整個MVC項目全部的異常都會被咱們的CustomExceptionAttribute類捕捉和處理。

單獨的做爲特性使用有一個前提,就是篩選器須要繼承FilterAttribute基類,上面演示代碼中已有說明,使用方法其餘的特性同樣,只須要在控制器或者相應的操做方法上方聲明便可:

[CustomException] public ActionResult Index() { return View(); }

這裏貼幾段引起異常的操做方法代碼,把下面代碼放到演示項目中默認的Home控制器中:

/// <summary> /// 拋出HTTP 500 /// </summary> /// <returns></returns> public ActionResult ThrowHttp500() { throw new HttpException(500, "服務器錯誤"); } /// <summary> /// 拋出HTTP 404 /// </summary> /// <returns></returns> public ActionResult ThrowHttp404() { throw new HttpException(404, "頁面未找到"); } /// <summary> /// 拋出未引用對象異常 /// </summary> /// <returns></returns> public ActionResult ThrowNullReferenceException() { throw new NullReferenceException(); } /// <summary> /// 引起輸入字符串的格式不正確異常 /// </summary> /// <returns></returns> public ActionResult ThrowFormatException() { string str = ""; int count = Convert.ToInt32(str); return View("Index"); }

網站生成運行後輸入路徑測試,結果以下圖:

實現IExceptionFilter接口的自定義異常過濾器演示結果

使用IExceptionFilter的一些注意事項

這裏總結使用IExceptionFilter編寫自定義異常過濾器的須要注意的地方:

1、並不依賴自定義異常錯誤,因此web.config配置文件中的customErrors  mode="On"或者mode="Off"都沒法影響到咱們本身編寫的過濾器的使用。

可是若是你想要使用customErrors來進行控制,那麼也很簡單,在咱們自定義的過濾器中,只須要使用filterContext.HttpContext.IsCustomErrorEnabled屬性進行判斷便可:

IExceptionFilter接口使用ExceptionHandled屬性防止多個異常過濾器衝突


2、只能處理Action操做方法裏拋出的異常,超出此範圍的異常是沒法被捕捉到的。這點很是重要,這是ASP.NET MVC中篩選器自身的特性。不管你是在Controller上聲明篩選器,或者經過全局註冊,最終都只能應用到某個具體的Action方法。這裏所謂的全局註冊,其實就是指全部的Action都應用了該篩選器。一樣的,若是篩選器在控制器上聲明,其實也是表示該控制器下全部的操做方法都要應用此篩選器。篩選器自己只針對ACtion方法!在Controller上聲明和全局註冊只是設定了其應用範圍!

這裏引用《ASP.NET MVC高級編程》的說明:

異常過濾器用來處理操做或結果執行期間可能拋出的異常。

注意:IExceptionFilter也能捕獲Action方法的其餘篩選器所拋出的異常。例如實現IAuthorizationFilterIActionFilter或者IResultFilter的篩選器若是執行期間拋出異常,都是能被捕捉到的。

PS:篩選器=過濾器!不一樣的翻譯別稱。

主要沒法處理的範圍和狀況有:

  1. 視圖中拋出的異常。
  2. IIS級別的錯誤(例如訪問不存在的靜態文件)。
  3. ASP.NET MVC的路由映射錯誤問題,例如訪問不存在的路由和url。

若是遇到上面的狀況又想捕獲到異常,也是有兩個解決辦法的,一個是使用Global.asax的Application_Error事件處理這部分篩選器沒法捕捉的異常。另一個是直接設置IIS上的.NET錯誤便可,其實就是設置自定義異常錯誤,設置web.config配置文件中的customErrors節點的mode="On",而後設置對應的error節點和相應的HTTP錯誤跳轉頁面。注意自定義篩選器的優先級順序要設置下,若是沒有什麼業務需求而且不考慮SEO就可使用這種簡單的方法。


3、IIS級的錯誤並不會被捕捉到,由於此類錯誤還未進入ASP.NET 的處理流程,這個問題上面第二點就有提到。好比下圖中輸入某個物理路徑不存在的頁面,這裏直接就顯示IIS默認的錯誤頁,須要在服務器上進行相關設置:

實現IExceptionFilter接口的異常過濾器沒法捕捉IIS級錯誤


4、在咱們的篩選器CustomExceptionAttribute代碼中,能夠看到使用了一個ExceptionHandled屬性:

if (filterContext.ExceptionHandled == true)

這個屬性十分重要,是爲了防止使用多個異常過濾器致使操做發生衝突。在使用多個異常過濾器的狀況下此屬性做用十分明顯!

若是爲true,則表示已有其餘filter處理過此異常 ,就不須要再次進行處理了,以避免覆蓋其餘filter所解決的Exception。

若是爲false,就要對該Exception進行相應的處理,處理以後將ExceptionHandled屬性設置爲true,表示異常已經處理,不然操做會被其餘異常過濾器覆蓋掉,形成衝突。

默認狀況下若是ExceptionHandled屬性沒有被其餘異常處理過濾器設置爲true ,那麼ASP.NET MVC將使用默認的異常處理,若是沒有開啓customErrors,將會顯示異常詳細頁面(錯誤黃頁)。


5、操做相關的頁面跳轉,若是網站要考慮搜索引擎優化方法,必定不要使用下面的兩種方法進行跳轉:

//filterContext.HttpContext.Response.Redirect("~/HttpError/500.html"); //filterContext.HttpContext.Server.Transfer("~/HttpError/500.html"); 

由於這兩種方法你設置了StatusCode是無效的。

此外要注意路由在傳統的ASP.NET 跳轉方法中存在不兼容問題,若是想要使用MVC的路由和視圖進行跳轉,能夠參考下面的代碼:

/*-------------------------------------------------------------------------- * 下面的範例是使用MVC控制器和視圖進行跳轉,具體可查閱HandleErrorAttribute源碼 --------------------------------------------------------------------------*/ string controllerName = (string)filterContext.RouteData.Values["controller"]; string actionName = (string)filterContext.RouteData.Values["action"]; HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName); filterContext.Result = new ViewResult { ViewName = "視圖名稱", MasterName = "母板名稱", ViewData = new ViewDataDictionary<HandleErrorInfo>(model), TempData = filterContext.Controller.TempData }; filterContext.ExceptionHandled = true; filterContext.HttpContext.Response.Clear(); filterContext.HttpContext.Response.StatusCode = 500;

上面的代碼在視圖結果中還傳入了錯誤信息實體,若是隻想要簡單的跳轉到某個視圖或控制器,其實只要設置ActionResult便可,代碼以下:

filterContext.Result = new ViewResult() { ViewName="視圖名稱"}; filterContext.ExceptionHandled = true; filterContext.HttpContext.Response.Clear(); filterContext.HttpContext.Response.StatusCode = 500;


出處:https://shiyousan.com/post/635833789557065314

版權聲明:本文采用知識共享許可協議:署名-相同方式共享 4.0 國際(CC BY-SA 4.0)。歡迎轉載本文,轉載請聲明出處或保留此段聲明。

相關文章
相關標籤/搜索