在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
至於FilterAttribute
抽象類,若是打算將編寫的異常過濾器做爲特性來使用,那麼它必須繼承自FilterAttribute
或者它的子類HandleErrorAttribute
。不做爲特性使用的全局操做過濾器能夠不繼承這個基類。學習
下圖演示能夠看到沒有繼承FilterAttribute
類就沒法做爲特性來使用:
可是不影響全局操做過濾器的註冊和使用:
記得使用NUGET將ASP.NET MVC 5 安裝到新類庫中,IExceptionFilter
是屬於System.Web.Mvc
程序集,並且編寫自定義的過濾器和特性會使用到不少與System.Web.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
編寫自定義異常過濾器的須要注意的地方:
1、並不依賴自定義異常錯誤,因此web.config配置文件中的customErrors mode="On"
或者mode="Off"
都沒法影響到咱們本身編寫的過濾器的使用。
可是若是你想要使用customErrors來進行控制,那麼也很簡單,在咱們自定義的過濾器中,只須要使用filterContext.HttpContext.IsCustomErrorEnabled
屬性進行判斷便可:
2、只能處理Action操做方法裏拋出的異常,超出此範圍的異常是沒法被捕捉到的。這點很是重要,這是ASP.NET MVC中篩選器自身的特性。不管你是在Controller上聲明篩選器,或者經過全局註冊,最終都只能應用到某個具體的Action方法。這裏所謂的全局註冊,其實就是指全部的Action都應用了該篩選器。一樣的,若是篩選器在控制器上聲明,其實也是表示該控制器下全部的操做方法都要應用此篩選器。篩選器自己只針對ACtion方法!在Controller上聲明和全局註冊只是設定了其應用範圍!
這裏引用《ASP.NET MVC高級編程》的說明:
異常過濾器用來處理操做或結果執行期間可能拋出的異常。
注意:IExceptionFilter
也能捕獲Action方法的其餘篩選器所拋出的異常。例如實現IAuthorizationFilter
,IActionFilter
或者IResultFilter
的篩選器若是執行期間拋出異常,都是能被捕捉到的。
PS:篩選器=過濾器!不一樣的翻譯別稱。
主要沒法處理的範圍和狀況有:
若是遇到上面的狀況又想捕獲到異常,也是有兩個解決辦法的,一個是使用Global.asax的Application_Error事件處理這部分篩選器沒法捕捉的異常。另一個是直接設置IIS上的.NET錯誤便可,其實就是設置自定義異常錯誤,設置web.config配置文件中的customErrors
節點的mode="On"
,而後設置對應的error
節點和相應的HTTP錯誤跳轉頁面。注意自定義篩選器的優先級順序要設置下,若是沒有什麼業務需求而且不考慮SEO就可使用這種簡單的方法。
3、IIS級的錯誤並不會被捕捉到,由於此類錯誤還未進入ASP.NET 的處理流程,這個問題上面第二點就有提到。好比下圖中輸入某個物理路徑不存在的頁面,這裏直接就顯示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)。歡迎轉載本文,轉載請聲明出處或保留此段聲明。