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中需要改成
View Code
/// <summary>
/// 標記自訂的AboutError
/// </summary>
/// <returns></returns>
[AboutError]
public ActionResult About()
{
return View();
}
經過一輪折騰,總算實現了需求,即保證了通用異常被正常處理,個別action個人化的錯誤頁面也能實現,還算比較整潔。但最好架構的HandleError能夠提供是否忽略判斷異常是否已被處理過的屬性設定。