ASP.NET MVC中的統一化自定義異常處理
當ASP.NET MVC程式出現了異常,怎麼處理更加規範?
1. 最簡單的方法是設定<customErrors/>節點
<customErrors>節點用於定義一些自定義錯誤資訊的資訊。此節點有Mode和defaultRedirect兩個屬性,其中defaultRedirect屬性是一個可選屬性,表示應用程式發生錯誤時重定向到的預設URL,如果沒有指定該屬性則顯示一般性錯誤。Mode屬性是一個必選屬性,它有三個可能值,它們所代表的意義分別如下: On 表示在本地和遠端使用者都會看到自定義錯誤資訊。 Off 禁用自定義錯誤資訊,本地和遠端使用者都會看到詳細的錯誤資訊。 RemoteOnly 表示本地使用者將看到詳細錯誤資訊,而遠端使用者將會看到自定義錯誤資訊。
這裡有必要說明一下本地使用者和遠端使用者的概念。當我們訪問asp.net應用程時所使用的機器和釋出asp.net應用程式所使用的機器為同一臺機器時成為本地使用者,反之則稱之為遠端使用者。在開發除錯階段為了便於查詢錯誤Mode屬性建議設定為Off,而在部署階段應將Mode屬性設定為On或者 RemoteOnly,以避免這些詳細的錯誤資訊暴露了程式程式碼細節從而引來黑客的入侵。
<error>子節點
在<customErrors>節點下還包含有<error>子節點,這個節點主要是根據伺服器的HTTP錯誤狀態程式碼而重定向到我們自定義的錯誤頁面,注意要使<error>子節點下的配置生效,必須將<customErrors>節點的Mode屬性設定為“On”。下面是一個例子:
<customErrors mode="On" defaultRedirect="GenericErrorPage.htm"> <error statusCode="403" redirect="403.htm" /> <error statusCode="404" redirect="404.htm" /> </customErrors>
在上面的配置中如果使用者訪問的頁面不存在就會跳轉到404.htm頁面,如果使用者沒有許可權訪問請求的頁面則會跳轉到403.htm頁面,403.htm和404.htm頁面都是我們自己新增的頁面,我們可以在頁面中給出友好的錯誤提示。
2. 使用過濾器HandleErrorAttribute
隨著ASP.NET MVC版本的更新,出現了HandleErrorAttribute,使用Filter以AOP的思想實現了針對於Action的異常處理。使用此 Filter後,當程式中出現異常的時候,會去封裝這些異常資訊,然後路由自動轉到該Controller對應的Error.cshtml中,如果此路徑下沒有改檔案,則會到shared目錄中尋找此檔案。另外一個相關的是在Global.asax中的protected void Application_Error[object sender, EventArgs e]方法,是捕捉異常的最後一道防線,也就是說,這是最高層次的異常捕獲處理邏輯。使用HandleErrorAttribute後,找到了Error.cshtml,則此時異常已經被捕獲處理,所以不會再次被 Application_Error捕獲處理。此外,可以通過@model HandleErrorInfo 在Error.cshtml中定製顯示異常資訊。有一點需要注意的是,HandleErrorAttribute是在customErrors基礎之上的,如果想使用 HandleErrorAttribute,customErrors的Mode必須要設定為On或RemoteOnly. 否則,HandleErrorAttribute將不起作用。
3. 自定義顯示路徑
如果遇到異常後不單單是要自定義異常顯示,而且還需要進行日誌記錄。此時,可以通過繼承擴充套件HandleErrorAttribute來實現新的 Filter,然後在lobal.ascx中對所有的Action進行註冊,filters.Add(new MyHandleErrorAttribute()).在這裡詳細說明一下另一種方式。我們可以寫一個專門的Controller和Action來進行異常發生時的路由設定.
public class UtilityController : Controller
{
public ActionResult Error()
{
return View();
}
public ActionResult PageNotFound()
{
return View();
}
}
|
當出現異常的時候,把異常拋到最頂端,由Application_Error統一處理。這裡的統一處理就包括,記錄日誌,重新進行頁面定向等。
protected void Application_Error(object sender, EventArgs e)
{
var ex = Server.GetLastError();
Log.Error(ex); //記錄日誌資訊
var httpStatusCode = (ex is HttpException) ? (ex as HttpException).GetHttpCode() : 500; //這裡僅僅區分兩種錯誤
var httpContext = ((MvcApplication)sender).Context;
httpContext.ClearError();
httpContext.Response.Clear();
httpContext.Response.StatusCode = httpStatusCode;
var shouldHandleException = true;
HandleErrorInfo errorModel;
var routeData = new RouteData();
routeData.Values["controller"] = "Utility";
switch (httpStatusCode)
{
case 404:
routeData.Values["action"] = "PageNotFound";
errorModel = new HandleErrorInfo(new Exception(string.Format("No page Found", httpContext.Request.UrlReferrer), ex), "Utility", "PageNotFound");
break;
default:
routeData.Values["action"] = "Error";
Exception exceptionToReplace = null; //這裡使用了EntLib的異常處理模組的一些功能
shouldHandleException = ExceptionPolicy.HandleException(ex, "LogAndReplace", out exceptionToReplace);
errorModel = new HandleErrorInfo(exceptionToReplace, "Utility", "Error");
break;
}
if (shouldHandleException)
{
var controller = new UtilityController();
controller.ViewData.Model = errorModel; //通過程式碼路由到指定的路徑
((IController)controller).Execute(new RequestContext(new HttpContextWrapper(httpContext), routeData));
}
}
}