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


.NET MVC全局異常處理

一直知道有.NET有相關的配置,但沒有實際做過,以為改下設定就可以,結果實際使用的時候還是遇到不少問題,所以要記錄一下。

IIS配置

剛開始不想改程序代碼,所以直接就想到了IIS里面的錯誤頁配置配置,一開始反復測試,設置改了很多,但是沒有效果,后來發現是靜態頁的配置,還沒有進入MVC的程序部分,所以對於.NET MVC這種動態頁是不生效的,應該使用.NET錯誤頁選項

靜態錯誤頁配置

靜態頁配置流程如下:



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

我們可以把IIS設置為均使用自定義錯誤頁看下效果,或者直接通過文件訪問


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

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

通過這個錯誤我們去搜索解決方法可以看到一些人說將web.config中的httperror節下的defaultPath解鎖即可,但似乎這是IIS7以前的設置,在IIS10中並沒有相應的選項,看到一些說明提到可能是官方使用了更加安全的管理機制,因為發現這邊的配置是靜態頁相關,不符合我的需要,沒有深入研究,如果一定要使用這種可以看看這篇博客,試試能否通過系統命令解決鎖定的問題

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");


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM