ASP.NET MVC 應用程序自定義錯誤頁面未顯示在共享託管環境中
我在共享主機上部署的 ASP.NET MVC 應用程序上遇到自定義錯誤問題。我創建了一個 ErrorController 並將以下程式碼添加到 Global.asax 以擷取未處理的異常,記錄它們,然後將控制權轉移到 ErrorController 以顯示自定義錯誤。此程式碼取自此處:
protected void Application_Error(object sender, EventArgs e) { Exception ex = Server.GetLastError(); Response.Clear(); HttpException httpEx = ex as HttpException; RouteData routeData = new RouteData(); routeData.Values.Add("controller", "Error"); if (httpEx == null) { routeData.Values.Add("action", "Index"); } else { switch (httpEx.GetHttpCode()) { case 404: routeData.Values.Add("action", "HttpError404"); break; case 500: routeData.Values.Add("action", "HttpError500"); break; case 503: routeData.Values.Add("action", "HttpError503"); break; default: routeData.Values.Add("action", "Index"); break; } } ExceptionLogger.LogException(ex); // <- This is working. Errors get logged routeData.Values.Add("error", ex); Server.ClearError(); IController controller = new ErrorController(); // The next line doesn't seem to be working controller.Execute(new RequestContext(new HttpContextWrapper(Context), routeData)); }Application_Error 肯定會觸發,因為日誌記錄工作正常,但我沒有顯示我的自定義錯誤頁面,而是獲得了 Go Daddy 通用錯誤頁面。從部落格文章的標題中,我注意到它使用了 MVC 框架的 Release Candidate 2。1.0 中是否發生了一些變化,導致最後一行程式碼不起作用?像往常一樣,它在我的機器上執行良好。
任何建議將不勝感激。
編輯:忘了提到我已經嘗試了 Web.config 中 customErrors 模式的所有 3 種可能性(Off、On 和 RemoteOnly)。無論此設置如何,結果都相同。
編輯 2:我也試過了
$$ HandleError $$控制器類上的裝飾。 更新:我已經弄清楚並修復了 404。Go Daddy 的主機控制中心的設置面板中有一個部分可以控制 404 行為,預設是顯示它們的通用頁面,顯然這會覆蓋任何 Web.config 設置。所以我的自定義 404 頁面現在按預期顯示。但是,500s 和 503s 仍然無法正常工作。如果 Sql Server 拋出異常,我在 HomeController 中有程式碼來獲取內容的靜態文本版本,如下所示:
public ActionResult Index() { CcmDataClassesDataContext dc = new CcmDataClassesDataContext(); // This might generate an exception which will be handled in the OnException override HomeContent hc = dc.HomeContents.GetCurrentContent(); ViewData["bodyId"] = "home"; return View(hc); } protected override void OnException(ExceptionContext filterContext) { // Only concerned here with SqlExceptions so an HTTP 503 message can // be displayed in the Home View. All others will bubble up to the // Global.asax.cs and be handled/logged there. System.Data.SqlClient.SqlException sqlEx = filterContext.Exception as System.Data.SqlClient.SqlException; if (sqlEx != null) { try { ExceptionLogger.LogException(sqlEx); } catch { // couldn't log exception, continue without crashing } ViewData["bodyId"] = "home"; filterContext.ExceptionHandled = true; HomeContent hc = ContentHelper.GetStaticContent(); if (hc == null) { // Couldn't get static content. Display friendly message on Home View. Response.StatusCode = 503; this.View("ContentError").ExecuteResult(this.ControllerContext); } else { // Pass the static content to the regular Home View this.View("Index", hc).ExecuteResult(this.ControllerContext); } } }這是嘗試獲取靜態內容的程式碼:
public static HomeContent GetStaticContent() { HomeContent hc; try { string path = Configuration.CcmConfigSection.Config.Content.PathToStaticContent; string fileText = File.ReadAllText(path); string regex = @"^[^#]([^\r\n]*)"; MatchCollection matches = Regex.Matches(fileText, regex, RegexOptions.Multiline); hc = new HomeContent { ID = Convert.ToInt32(matches[0].Value), Title = matches[1].Value, DateAdded = DateTime.Parse(matches[2].Value), Body = matches[3].Value, IsCurrent = true }; } catch (Exception ex) { try { ExceptionLogger.LogException(ex); } catch { // couldn't log exception, continue without crashing } hc = null; } return hc; }我已經驗證,如果我更改連接字元串以生成 SqlException,程式碼會正確記錄錯誤,然後抓取並顯示靜態內容。但是,如果我還更改了 Web.config 中靜態文本文件的路徑以測試 503 版本的主視圖,那麼我得到的是一個除了“服務不可用”之外什麼都沒有的頁面。而已。沒有具有網站外觀的自定義 503 消息。
有沒有人對可能有幫助的程式碼改進有任何建議?向 HttpResponse 添加不同的標頭會有所幫助嗎?還是 Go Daddy 強硬地劫持了 503?
我找到了解決方案,而且非常簡單。原來問題實際上出在 IIS7 中。在 Visual Studio 中調試此問題時,我看到了 HttpResponse 對象的一個我以前沒有註意到的屬性:
public bool TrySkipIisCustomErrors { get; set; }這將我引向離我最近的搜尋引擎,該搜尋引擎找到了 Rick Strahl 的一篇很棒的博文,以及angeringpets.com上的另一篇博文,以及SO 上的這個問題。這些連結比我能更好地解釋血淋淋的細節,但是 Rick 文章中的這句話很好地捕捉到了它:
真正的混亂發生在這裡,因為錯誤被 ASP.NET 擷取,但最終仍然由 IIS 處理,它查看 500 狀態程式碼並返回庫存 IIS 錯誤頁面。
這種行為似乎也特定於集成模式下的 IIS7。來自msdn:
在 IIS 7.0 中以經典模式執行時,TrySkipIisCustomErrors 屬性預設值為 true。在集成模式下執行時,TrySkipIisCustomErrors 屬性預設值為 false。
所以基本上我最終要做的就是
Response.TrySkipIisCustomErrors = true;在任何設置Response.StatusCode為 500 或 503 的程式碼之後添加,現在一切都按設計執行。