[TOC] .NET MVC全局異常處理 一直知道有.NET有相關的配置,但沒有實際做過,以為改下設定就可以,結果實際使用的時候還是遇到不少問題,所以要記錄一下。 IIS配置 剛開始不想改程式代碼,所以直接就想到了IIS裡面的錯誤頁配置配置,一開始反覆測試,設置改了很多,但是沒有效果,後來發現是靜態 ...
目錄
.NET MVC全局異常處理
一直知道有.NET有相關的配置,但沒有實際做過,以為改下設定就可以,結果實際使用的時候還是遇到不少問題,所以要記錄一下。
IIS配置
剛開始不想改程式代碼,所以直接就想到了IIS裡面的錯誤頁配置配置,一開始反覆測試,設置改了很多,但是沒有效果,後來發現是靜態頁的配置,還沒有進入MVC的程式部分,所以對於.NET MVC這種動態頁是不生效的,應該使用.NET錯誤頁選項
靜態錯誤頁配置
靜態頁配置流程如下:
如上圖所示,IIS中配置對錯誤的響應有三種方式,預設情況是第三個,本地訪問顯示詳細錯誤信息,外部地址訪問顯示自定義頁面,這樣方便開發者調試,如果沒有設置專門的錯誤頁會使用IIS自帶的樣式,也就是第二張圖中的配置,根據路徑我們可以找到這樣一個文件夾,裡面都是錯誤提示的靜態頁,對應不同的狀態代碼
我們可以把IIS設置為均使用自定義錯誤頁看下效果,或者直接通過文件訪問
上面那張是詳細的靜態404錯誤,可以看到會暴露我們系統路徑,下麵則是預設的自定義錯誤頁
靜態錯誤的預設頁有相應的設置,看似可以修改,有“文件”、“執行URL”、“重定向”三種,但是實際設置一下就會發現報錯:鎖定錯誤
通過這個錯誤我們去搜索解決方法可以看到一些人說將web.config中的httperror節下的defaultPath解鎖即可,但似乎這是IIS7以前的設置,在IIS10中並沒有相應的選項,看到一些說明提到可能是官方使用了更加安全的管理機制,因為發現這邊的配置是靜態頁相關,不符合我的需要,沒有深入研究,如果一定要使用這種可以看看這篇博客,試試能否通過系統命令解決鎖定的問題
.NET錯誤頁配置
.NET錯誤頁的設置與靜態頁差不多,除了入口不一樣,配置的選項也不太相同,但是整體意思一樣
可以看到這裡要求是絕對URL,所以實際使用起來應該是不太方便,所以沒有找到太多相關資料。另外,需要web.conig中的customError設為On,部分異常如500會自動跳轉到MVC的預設錯誤頁Home/Error
使用IIS的錯誤頁處理雖然不用改代碼,但是維護起來局限性很多,最終還是應該通過程式進行全局異常捕獲
程式設置
通過程式控制的方法我想到兩種,一個是使用全局配置文件Global.asax中的Application_Error方法,另一個是使用MVC的過濾器,預設的四種過濾器中就包含異常過濾
全局異常配置
這種方法對於WebForm和MVC都是通用的,在ASP.NET中,只要網站程式拋出未捕獲的異常都會觸發Application_Error事件。
使用此方法一定要把GlobalFilter全局過濾器中的HandleErrorAttribute註冊取消掉,也可以將配置文件中的customErrors節點關閉,否則HTTP 500的錯誤將不會被Application_Error事件捕獲。
捕獲到異常之後我們可以很容易地跳轉到靜態頁面
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
var httpStatusCode = (exception as HttpException)?.GetHttpCode() ?? 700; //如果為空則走自定義
var httpContext = ((MvcApplication)sender).Context;
httpContext.ClearError();
switch (httpStatusCode)
{
case 404:
httpContext.Response.Redirect("~/Error/404.htm");
break;
default:
httpContext.Response.Redirect("~/Error/500.htm");
break;
}
}
在一般情況下我們也可以指向一個控制器
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
var httpStatusCode = (exception as HttpException)?.GetHttpCode() ?? 700; //如果為空則走自定義
var httpContext = ((MvcApplication)sender).Context;
httpContext.ClearError();
var routeDic = new RouteValueDictionary
{
{"controller", "Home"},
{ "action","Error"}
};
httpContext.Response.RedirectToRoute("Default", routeDic);
}
但是在實際的業務中遇到了一些http請求的問題,在處理一部分代碼拋出的異常時會出現“伺服器無法在已發送HTTP標頭之後······”這一系列異常,如“設置狀態”、“追加標頭”等,這個時候跳轉要使用另一種寫法
protected void Application_Error(object sender, EventArgs e)
{
Server.ClearError();
Response.TrySkipIisCustomErrors = true;
var routeData = new RouteData();
IController controller = new HomeController();
routeData.Values.Add("controller", "Home");
routeData.Values.Add("action", "Error");
controller.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
Response.End();
}
這裡要註意的一點是如果要使用Area中的控制器不能寫成routeData.Values.Add,而是使用DataTokens
routeData.DataTokens.Add("area", "TestArea");