2011-02-06 24 views
37

我一直在努力整天在我的ASP.NET MVC 2應用程序中實現錯誤處理。我已經看過各種技術,但都沒有正常工作。我使用的是MVC2和.NET 4.0(在MVC3發佈之前啓動了該項目;我們將在我們發佈初始版本後進行升級)。如何在.NET MVC 2中實現正確的HTTP錯誤處理?

在這一點上,我會很樂意妥善處理404和500錯誤 - 403(需要授權)也會很好,其次是各種其他特定的響應。現在,我要麼得到所有404,500S所有,404之前的所有302S,或全部302S前500

這裏是我的要求(這應該是非常接近HTTP的基本要求):

  • 如果找不到資源,請拋出404,並顯示404請求的URL的特定頁面。不要返回像302這樣的中間響應代碼。理想情況下,請保留請求的URL,而不是顯示像/Error/NotFound這樣的新URL - 但是如果後者顯示,請確保我們沒有返回重定向響應來獲取它。

  • 如果發生內部服務器錯誤,請拋出500,並顯示500特定的錯誤,並指出錯誤的位置。同樣,不要返回中間響應代碼,理想情況下不要更改URL。

這裏是我會考慮一個404:

  1. 沒有找到靜態文件:/Content/non-existent-dir/non-existent-file.txt
  2. 控制器未發現:/non-existent-controller/Foo/666
  3. 控制器發現,但找不到動作:/Home/non-existent-action/666
  4. 找到控制器和操作,但操作無法找到請求的對象:/Home/Login/non-existent-id

這裏是我會考慮一個500:

  • 發表錯誤值:POST /User/New/new-user-name-too-long-for-db-column-constraint
  • 非數據相關的問題,如Web服務端點沒有響應
  • 其中一些問題需要通過特定的控制器或模型來標識,然後控制器應該拋出相應的HttpException。其餘的應該更通用地處理。

    對於404情況#2,我試圖使用自定義的ControllerFactory來拋出404如果無法找到控制器。 對於404案#3,我試圖用一個自定義的基本控制器覆蓋HandleUnknownAction並拋出一個404

    在這兩種情況下,我得到的404前302,我從來沒有得到500錯誤;如果我修改Web.config在我的Web服務端點中輸入錯字,我仍然會得到一個302,然後404代表一個URL(控制器/操作),其中使用 Web服務無法找到。 我也得到了請求的URL爲(N不必要的)查詢字符串PARAM:/Error/NotFound?aspxerrorpath=/Home/non-existent-action

    這兩種技術都來自http://www.niksmit.com/wp/?p=17(如何獲得正常的404(使用ASP找不到網頁)錯誤頁面。Net MVC),指向http://richarddingwall.name/2008/08/17/strategies-for-resource-based-404-errors-in-aspnet-mvc/

    如果在Web.config中有<customErrors mode="On" defaultRedirect="~/Error/Unknown" redirectMode="ResponseRedirect" />,我得到相應的響應代碼,但是我的Error控制器永遠不會被調用。取出redirectMode屬性得到我的MVC錯誤的觀點,但有一箇中間的302和改變URL - 總是相同的控制器(Unknown = 500;如果我將其更改爲NotFound一切看起來像404)。

    下面是一些我讀過的和其他的東西試圖執行:

    ..以及一堆StackOverflow帖子。

    對我來說,這種錯誤處理對於Web應用程序來說是非常基本的,而MVC框架應該有默認的功能,並且讓其他人可以擴展它。也許他們會在未來的版本中做到這一點。同時,有人可以給我詳細的介紹如何實現正確的HTTP響應嗎?

    回答

    46

    以下是您可以使用的一種技術。定義一個ErrorsController這將有助於錯誤頁面:

    public class ErrorsController : Controller 
    { 
        public ActionResult Http404() 
        { 
         Response.StatusCode = 404; 
         return Content("404", "text/plain"); 
        } 
    
        public ActionResult Http500() 
        { 
         Response.StatusCode = 500; 
         return Content("500", "text/plain"); 
        } 
    
        public ActionResult Http403() 
        { 
         Response.StatusCode = 403; 
         return Content("403", "text/plain"); 
        } 
    } 
    

    ,然後在Global.asax你可以認購Application_Error事件在那裏你可以記錄異常和執行ErrorsController的相應動作:

    protected void Application_Error(object sender, EventArgs e) 
    { 
        var app = (MvcApplication)sender; 
        var context = app.Context; 
        var ex = app.Server.GetLastError(); 
        context.Response.Clear(); 
        context.ClearError(); 
        var httpException = ex as HttpException; 
    
        var routeData = new RouteData(); 
        routeData.Values["controller"] = "errors"; 
        routeData.Values["exception"] = ex; 
        routeData.Values["action"] = "http500"; 
        if (httpException != null) 
        { 
         switch (httpException.GetHttpCode()) 
         { 
          case 404: 
           routeData.Values["action"] = "http404"; 
           break; 
          case 403: 
           routeData.Values["action"] = "http403"; 
           break; 
          case 500: 
           routeData.Values["action"] = "http500"; 
           break; 
         } 
        } 
        IController controller = new ErrorsController(); 
        controller.Execute(new RequestContext(new HttpContextWrapper(context), routeData)); 
    } 
    

    現在剩下的就是開始拋出適當的例外:

    public class HomeController : Controller 
    { 
        public ActionResult Index() 
        { 
         throw new HttpException(404, "NotFound"); 
        } 
    } 
    
    +1

    看起來不錯 - 我會在幾個小時內給它一次,讓你知道它是如何工作的。有一件事我沒有看到:你在哪裏手柄控制器的情況下,沒有發現? – Val 2011-02-06 17:11:05

    +2

    @val,當控制器沒有發現與代碼404的HttpException自動由ASP拋出。NET MVC所以它會進入第一種情況。 – 2011-02-06 22:21:34

    0

    這不回答你的問題,但要注意的是HTTP狀態500是非常重要的指示出現了錯誤的服務器上,所以你的例子:

    POST /User/New/new-user-name-too-long-for-db-column-constraint 
    

    是無效的理由拋出500 ,它是一個數據驗證問題,應該由MVC數據註釋或者jQuery驗證框架等來處理。只是在TextBox旁邊顯示一條錯誤消息,說「User Name too long」好得多。

    +1

    是的,我應該* *驗證和拋出一個驗證錯誤。但是,如果我無法驗證,然後從數據庫得到一個約束的錯誤時,它未能插入或更新行,我不爲正確檢查,我想拋出一個內部服務器錯誤。事實上,這就是我發現我們錯過了一些驗證! – Val 2011-02-06 17:07:59

    相關問題