.NET MVC全局異常處理(一)

.NET MVC全局異常處理

一直知道有.NET有相關的配置,但沒有實際作過,覺得改下設定就能夠,結果實際使用的時候仍是遇到很多問題,因此要記錄一下。安全

IIS配置

剛開始不想改程序代碼,因此直接就想到了IIS裏面的錯誤頁配置配置,一開始反覆測試,設置改了不少,可是沒有效果,後來發現是靜態頁的配置,尚未進入MVC的程序部分,因此對於.NET MVC這種動態頁是不生效的,應該使用.NET錯誤頁選項服務器

靜態錯誤頁配置

靜態頁配置流程以下:mvc



如上圖所示,IIS中配置對錯誤的響應有三種方式,默認狀況是第三個,本地訪問顯示詳細錯誤信息,外部地址訪問顯示自定義頁面,這樣方便開發者調試,若是沒有設置專門的錯誤頁會使用IIS自帶的樣式,也就是第二張圖中的配置,根據路徑咱們能夠找到這樣一個文件夾,裏面都是錯誤提示的靜態頁,對應不一樣的狀態代碼app

咱們能夠把IIS設置爲均使用自定義錯誤頁看下效果,或者直接經過文件訪問測試


上面那張是詳細的靜態404錯誤,能夠看到會暴露咱們系統路徑,下面則是默認的自定義錯誤頁網站

靜態錯誤的默認頁有相應的設置,看似能夠修改,有「文件」、「執行URL」、「重定向」三種,可是實際設置一下就會發現報錯:鎖定錯誤.net

經過這個錯誤咱們去搜索解決方法能夠看到一些人說將web.config中的httperror節下的defaultPath解鎖便可,但彷佛這是IIS7之前的設置,在IIS10中並無相應的選項,看到一些說明提到多是官方使用了更加安全的管理機制,由於發現這邊的配置是靜態頁相關,不符合個人須要,沒有深刻研究,若是必定要使用這種能夠看看這篇博客,試試可否經過系統命令解決鎖定的問題3d

win7 IIS Web.config節點鎖定問題調試

.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");
相關文章
相關標籤/搜索