使用HandleErrorAttribute處理異常

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

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

HandleErrorAttribute初步使用

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

<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控制器下複製以下代碼:mvc

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"); } }

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

首頁操做演示界面以下:ide

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

靈活運用HandleError特性

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

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 40四、40一、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

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

相關文章
相關標籤/搜索