Asp.net-Mvc

ASP.NET MVC 應用程序自定義錯誤頁面未顯示在共享託管環境中

  • June 9, 2016

我在共享主機上部署的 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 的程式碼之後添加,現在一切都按設計執行。

引用自:https://stackoverflow.com/questions/1706934