使用HandleErrorAttribute處理異常


ASP.NET MVC 默認提供了一個異常過濾器HandleError特性,使用該特性可以極為方便的捕捉並處理控制器和操作拋出的異常,也可以將此特性注冊為全局異常過濾器從而捕捉項目中的所有異常。如果想要簡單的消滅錯誤黃頁(錯誤詳細頁),使用HandlerErrorAttribute是不錯的選擇!

本文演示項目下載地址:GlobalExceptionHandle-By-HandleErrorAttribute.zip,項目使用的VS2013和ASP.NET MVC 5框架,項目中也拷貝了HandleErrorAttribute的源碼以供參考。

HandleErrorAttribute初步使用

使用HandleErrorAttribute處理異常很簡單,首先要開啟Web.config配置文件中的自定義錯誤,因為HandleError特性是依賴自定義錯誤的,customErrors的Mode必須要設置為On或RemoteOnly:

<customErrors mode="On" defaultRedirect="~/Error/Index"></customErrors>

到了這里基本上就成功啟用了異常過濾器,因為VS2013新建的ASP.NET MVC 5項目默認將HandleError注冊為全局異常過濾器,只要項目中的控制器和操作方法有拋出異常,默認就會被HandleError特性捕獲,從而跳轉到默認的錯誤詳細頁面~/Views/Shared/Error.cshtml。打開項目下App_Start的FilterConfig類(全局過濾器配置類),可以看到已經被注冊的HandleErrorAttribute類:

FilterConfig類中全局注冊了HandleErrorAttribute類

public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); //如果要更改默認的錯誤視圖,需要設置View屬性 //filters.Add(new HandleErrorAttribute() { View = "~/Views/Error/CustomHttpError.cshtml" }); } }

備注:關於FilterConfig類,如果不清楚的朋友可以看這篇文章:ASP.NET MVC 5 學習筆記之FilterConfig類

這里可以動態拋出一些異常來進行測試,在默認的Home控制器下復制如下代碼:

public class HomeController : Controller { public ActionResult Index() { return View(); } /// <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> /// 拋出未引用對象異常 /// ,此處單獨使用HandleError特性 /// ,並指定異常類型及響應視圖 /// </summary> /// <returns></returns> [HandleError(ExceptionType = typeof(NullReferenceException),View="CustomError")] public ActionResult ThrowNullReferenceException() { throw new NullReferenceException(); } /// <summary> /// 引發輸入字符串的格式不正確異常 /// ,此處指定了響應的錯誤頁面 /// ,由於是不同的控制器所以要完整的相對路徑 /// </summary> /// <returns></returns> [HandleError(View = "~/Views/Error/CustomHttpError.cshtml")] public ActionResult ThrowFormatException() { string str = ""; int count = Convert.ToInt32(str); return View("Index"); } }

備注:記得添加對應的錯誤視圖頁和控制器,建議直接下載演示項目。

首頁操作演示界面如下:

ASP.NET MVC 5 中HandleErrorAttribute類演示項目

靈活運用HandleError特性

HandleError特性提供了許多屬性,我們可以更加靈活的處理項目中拋出的異常。文檔截圖:

HandleError的屬性截圖

一般來說最常用的屬性是View,Order和ExceptionType,至於AllowMultiple屬性,除非業務有特殊要求,不然默認都是允許的。

自定義錯誤信息頁面

先說View屬性,設置此屬性就可以自定義要跳轉的錯誤視圖頁,否則一旦有異常拋出,默認會跳轉到Error.cshtml這個頁面,該頁面路徑為:~/Views/Shared/Error.cshtml。

設置View屬性一定要注意路徑問題,如果跳轉的異常信息頁面屬於其他控制器,即控制器路由地址不同,那么一定要用完整的相對路徑,否則引發二次異常:未找到視圖**或其母版視圖,或沒有視圖引擎支持搜索的位置。,比如下面代碼(此聲明是在Home控制器中,但是要跳轉的錯誤頁面則是在Error控制器中):

[HandleError(View = "~/Views/Error/CustomHttpError.cshtml")]

如果跳轉的異常頁面是屬於當前控制器,或者是屬於公共視圖頁的,就可以直接設置視圖名稱,代碼如下:

[HandleError(View="CustomError")]

備注:CustomError是一個公共的自定義錯誤信息視圖頁面,另外公共視圖頁都是放在~/Views/Shared/這個路徑下。

如果不想使用默認的Error.cshtml,想將所有的異常響應跳轉到自定義的異常信息視圖頁,那么只要通過全局過濾器配置類FilterConfig,在注冊HandleErrorAttribute時設置View屬性即可,代碼如下:

public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute() { View = "~/Views/Error/CustomHttpError.cshtml" }); } }

同樣要注意視圖路徑問題,其實這里最好使用公共視圖頁。這里額外說下Master屬性,此屬性可以指定要跳轉的視圖頁的母板。基本上很少用,除非項目業務有需要,比如動態控制母板什么的,不然的話都是在視圖中直接設置好的。

獲取詳細的異常信息

HandleErrorInfo類是HandleErrorAttribute默認提供的錯誤信息實體

HandleErrorInfo類屬性截圖

可以通過此類獲取引發異常的控制器和操作方法名稱,以及通過獲取Exception對象獲取詳細的錯誤內容。只要在對應的視圖頁中聲明此類即可,演示代碼:

@model System.Web.Mvc.HandleErrorInfo
@{
    ViewBag.Title = "自定義的公共錯誤頁面";
}
@section styles{
    <style type="text/css"> p {padding: 10px;} </style> } <h2>自定義的公共錯誤頁面</h2> @if (Model != null) { <p class="bg-danger text-danger"> 異常類型:@Model.Exception.GetType().Name </p> <p class="bg-danger text-danger"> 觸發異常的控制器:@Model.ControllerName </p> <p class="bg-danger text-danger"> 觸發異常的操作方法:@Model.ActionName </p> <p class="bg-danger text-danger"> 錯誤信息:@Model.Exception.Message </p> <p class="bg-info text-info"> 頁面路徑:~/Views/Shared/CustomError.cshtml </p> }

演示的視圖截圖:

ASP.NET MVC 5演示項目截圖

針對異常類型

ExceptionType屬性可以讓HandleError針對某種異常類型做出處理,可以搭配View屬性來使用,便於跳轉到不同的異常信息頁面,演示代碼:

[HandleError(ExceptionType = typeof(NullReferenceException),View="CustomError")]

上面代碼一旦捕獲到NullReferenceException異常,就會跳轉到CustomError視圖頁。注意一點,ExceptionType需要使用typeof轉化異常類型。

關於過濾器的執行順序

如果聲明了多個HandleError異常過濾器,就需要使用到Order屬性設置運行順序。Order屬性默認值為-1,也是最高優先級,正常來說整數值越大優先級越低,但是由於HandleError是繼承於IExceptionFilter接口,所以優先級順序是相反的,也就是說整數值越大,優先級也就越大,這里分享下官方文檔的資料

在 ASP.NET MVC 版本 1 和 2 中,OnException(ExceptionContext) 篩選器以正向順序運行。 在 ASP.NET MVC 版本 3 及更高版本中,此順序已反轉。 ASP.NET MVC 中的異常篩選器的行為類似於 .NET Framework 中的異常處理程序,后者由內而外展開。

這里有兩點需要注意:

  1. 如果Order屬性小於-1會拋出異常,所以只能設置大於等於-1的整數值。
  2. 如果沒有設置Order屬性,由於默認值都是-1(即最高優先級),會導致過濾器的執行順序變成無序隨機。

使用HandleErrorAttribute的一些注意事項

雖然HandleError特性使用起來很簡單,但是依然有很多需要注意很多地方,下面會詳細的分析特性的局限性和一些缺點。

依賴於ASP.NET的自定義錯誤模塊

如果customErrors mode="Off",則HandleError則無效,不對任何異常進行處理。

源碼:

// If custom errors are disabled, we need to let the normal ASP.NET exception handler // execute so that the user can see useful debugging information. if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled) { return; }

從源碼中可以看到,當HttpContext.IsCustomErrorEnabled屬性為false時(即未啟用自定義錯誤),就不進行任何處理,直接return停止流程。

只能處理500服務器錯誤

像HTPP 404、401、503等錯誤代碼,都是不處理的。源碼可以很直接看出,只要不是500錯誤就直接return返回:

// If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method), // ignore it. if (new HttpException(null, exception).GetHttpCode() != 500) { return; }

因此除了500錯誤,其他錯誤只能通過自定義錯誤模塊配置響應的頁面:

<customErrors mode="On" defaultRedirect="~/Error/Index"> <error redirect="~/Error/NotFound" statusCode="404"/> <error redirect="~/Error/NotFound" statusCode="400"/> </customErrors>

只能跳轉到視圖

HandleError只提供了一個View屬性,所以響應的異常信息頁也只能是視圖,其他的靜態頁面或者.aspx之類的都是無法進行跳轉的,可以使用自定義異常過濾器實現此功能。

跳轉的原理是通過設置ExceptionContext.Result屬性:

filterContext.Result = new ViewResult { ViewName = View, MasterName = Master, ViewData = new ViewDataDictionary<HandleErrorInfo>(model), TempData = filterContext.Controller.TempData };

不利於SEO搜索引擎優化

這里涉及到部分SEO的知識,簡單來說錯誤頁面是使用302跳轉,因此對搜索引擎抓取網站內容並不友好。如果是互聯網行業的網站,考慮到SEO的問題還是不要用HandleErrorAttribute處理異常,類似企業系統的網站倒是十分適合使用。

至於應對的方法可以參考此文:ASP.NET MVC全局異常處理和捕獲的思路

同時聲明多個HandleError的效果

多個HandleError最終只有一個過濾器會執行,其他的過濾器會自動停止執行,至於是哪個過濾器執行則是依靠Order屬性進行優先級確定,原理是使用ExceptionContext.ExceptionHandled屬性進行判斷,一旦該屬性為true則表示當前拋出的異常已經被其他過濾器處理了,直接return停止余下的流程。

高級進階之繼承HandleErrorAttribute實現自定義功能

通過繼承HandleErrorAttribute類創建新的異常處理特性,可以使我們擴展更多的自定義功能,比如記錄錯誤日志、發送錯誤信息郵件等。繼承后只要重寫此類中的OnException方法即可實現功能,演示代碼如下:

public class CustomHandleErrorAttribute : HandleErrorAttribute { public override void OnException(ExceptionContext filterContext) { /* 調用基類的OnException方法,實現基礎的功能。 * 如果要完全的自定義,就不需要調用基類的方法 */ base.OnException(filterContext); /* 此處可進行記錄錯誤日志,發送錯誤通知等操作 * 通過Exception對象和HttpException對象可獲取相關異常信息。 * Exception exception = filterContext.Exception; * HttpException httpException = new HttpException(null, exception); */ } }

調用基類的OnException方法依然要注意HandleErrorAttribute類的一些限制,除非不使用基類的方法。繼承后的新特性使用方法和HandleError一樣,依然可以全局注冊和局部使用。

如果需要完全自定義異常處理的功能,建議直接繼承IExceptionFilter接口以實現功能,可以參考這篇文章:ASP.NET MVC實現IExceptionFilter接口編寫自定義異常處理過濾器


參考資料分享

  1. HandleErrorAttribute源碼
  2. HandleErrorAttribute類
  3. GlobalFilterCollection類
  4. Exception對象

作者:十有三

出處:http://shiyousan.com/post/635838881238204198

歡迎轉載本文,本文版權歸作者所有,轉載請聲明出處或保留此段聲明。^_^請尊重他人勞動成果,共建美好的網絡環境。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM