aspnet mvc的錯誤處理方式主要有以下兩種
方式一:通過對controller或者action標記HandleError屬性,然后指定一個錯誤頁即可。這種方式最簡單,不需要額外增加action ,僅僅需要增加錯誤頁,但是不能記錄日志(因為沒有action,其實在aspx中也可調用記錄日志的方法)。這個錯誤頁還可以定義為強類型,類型為HandleErrorInfo,具體的Model又框架傳遞,可獲取具體的異常信息。
HandlError /// <summary> /// 標記了HandleError,並指明錯誤處理頁為AboutError.aspx /// </summary> /// <returns></returns> [HandleError(View = "AboutError")] public ActionResult About() { return View(); } AboutError.aspx <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<HandleErrorInfo>" %> <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> AboutError </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> 通過強類型獲取異常信息 <%= Model.Exception.Message %> </asp:Content>
這種方式比較靈活,比如需要對某個action定義個錯誤頁,就可采用這種方式。不過會有個小問題,后面會提到。
方式二:重寫controller類的onException方式,這種方式最直接了,通常用於一個項目的BaseController中,那么以后的controller都繼承這個類即可,可以在cs代碼中記錄錯誤日志,但是要定義錯誤頁的action和具體的錯誤頁面。方法中需要設置ExceptionHandled=true,否則錯誤會被拋到外層,這時候只能通過傳統的aspnet錯誤方式處理了,通常是招CustomerError中配置的錯誤頁,如果沒有配置,那就出現一個大黃頁了。ExceptionHandled=true這個操作會是邏輯有微妙的變化,后續提到。
重寫OnException protected override void OnException(ExceptionContext filterContext) { // 標記異常已處理 filterContext.ExceptionHandled = true; // 跳轉到錯誤頁 filterContext.Result = new RedirectResult(Url.Action("Error", "Shared")); }
講了這兩種方式,主要是為了講這兩種方式混用時會出現的問題。如果以action標記了HandleError屬性,同時期所在的controller又重寫了OnException方法,最終會怎樣處理呢?按照mvc中filter的執行順序,controller重寫的方法會被優先執行,不考慮action中的order順序,執行完畢之后再執行action標記的filter的方法。ok,有了這個理論之后,再看看之前提到的情況的執行順序。首先執行OnException中的處理方式,這時候filterContext.ExceptionHandled已經被標記為true了,再執行HandleError屬性的方法時,就不會在被執行了,也就是說自定義的錯誤頁白費了,不起作用。這是因為內置的HandleError在執行的時候會先判斷filterContext.ExceptionHandled是否為true,為true就不執行了,因此會出現一些很奇怪的bug,明白這個道理就知道如何處理了。
但是總不能把filterContext.ExceptionHandled = true;這行代碼去掉,因為其他action沒有標記handle error屬性,如果不使filterContext.ExceptionHandled為true, 那么錯誤還是會拋到外層,又交給CustomerError處理了,還是白搭。因此既要保持基類的OnException方法,又要有action自己個性化的錯誤頁,是不能使用系統內置的方式處理,只能自己再去定義ExceptionFilter 了,就是方式三。
方式三,自定義ExceptionFilter,需要繼承FilterAttribute,和實現IExceptionFilter接口,實現中不需要判斷Exception是否已處理,但要注意需要有AboutError這個action。
自定義ExceptionFilter public class AboutErrorAttribute : FilterAttribute, IExceptionFilter { #region IExceptionFilter 成員 public void OnException(ExceptionContext filterContext) { UrlHelper url = new UrlHelper(filterContext.RequestContext); filterContext.Result = new RedirectResult(url.Action("AboutError", "AboutError")); } #endregion }
那么action中需要改成
/// <summary> /// 標記自定義的AboutError /// </summary> /// <returns></returns> [AboutError] public ActionResult About() { return View(); }
經過一輪折騰,總算實現了需求,即保證了通用異常被正常處理,個別action個性化的錯誤頁面也能實現,還算比較整潔。但最好框架的HandleError能夠提供是否忽略判斷異常是否已被處理過的屬性設置。