如何在 .NET MVC 2 中实现正确的 HTTP 错误处理?
Posted
技术标签:
【中文标题】如何在 .NET MVC 2 中实现正确的 HTTP 错误处理?【英文标题】:How to implement proper HTTP error handling in .NET MVC 2? 【发布时间】:2011-06-22 02:59:49 【问题描述】:我整天都在努力在我的 ASP.NET MVC 2 应用程序中实现错误处理。我看过各种技术,但没有一个能正常工作。我正在使用 MVC2 和 .NET 4.0(在 MVC3 发布之前启动项目;我们将在发布初始版本后升级)。
此时,我很乐意正确处理 404 和 500 错误 - 403(需要授权)也很好,然后是各种其他具体响应。现在,我要么得到所有 404,所有 500,所有 404 之前的 302,或者 500 之前的所有 302。
这是我的要求(应该非常接近 HTTP 的基本要求):
如果找不到资源,则抛出 404,并显示带有请求 URL 的 404 特定页面。不要返回像 302 这样的中间响应代码。理想情况下,保留请求的 URL,而不是显示像 /Error/NotFound
这样的新 URL——但如果显示后者,请确保我们没有返回重定向响应来获取它。
如果发生内部服务器错误,则抛出 500,并显示特定于 500 的错误,并指出出现了什么问题。同样,不要返回中间响应代码,最好不要更改 URL。
这是我认为的 404:
-
找不到静态文件:
/Content/non-existent-dir/non-existent-file.txt
找不到控制器:/non-existent-controller/Foo/666
找到控制器,但未找到操作:/Home/non-existent-action/666
找到控制器和动作,但动作找不到请求的对象:/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,表示找不到 Web 服务的 URL(控制器/操作)使用 .
我还将请求的 URL 作为(n 不需要的)查询字符串参数:/Error/NotFound?aspxerrorpath=/Home/non-existent-action
这两种技术都来自http://www.niksmit.com/wp/?p=17(How to get normal 404 (Page not found) error pages using 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" />
,我会得到相应的响应代码,但我的错误控制器永远不会被调用。取出 redirectMode
属性可以让我看到 MVC 错误视图,但中间有 302 和更改的 URL - 并且始终是相同的控制器(Unknown
= 500;如果我将其更改为 NotFound
一切看起来像 404 )。
以下是我阅读并尝试实现的其他一些内容:
http://www.davidjuth.com/asp-net-mvc-error-handler.aspx http://sanjayuttam.com/wordpress/index.php/c-sharp/c-sharp-code-examples/error-handling-in-asp-net-mvc-1-part-2-of-2/ http://blog.hebbink.com/post/2010/12/14/NET-custom-404-error-page-returns-302-for-http-status.aspx http://blog.dantup.com/2009/04/aspnet-mvc-handleerror-attribute-custom.html http://weblogs.asp.net/scottgu/archive/2008/07/14/asp-net-mvc-preview-4-release-part-1.aspx.. 以及一堆 *** 帖子。
在我看来,这种错误处理对于 Web 应用程序来说是非常基本的,MVC 框架应该有默认的开箱即用的方法,并让人们扩展它以使其工作。也许他们会在未来的版本中这样做。同时,有人可以给我详细介绍如何实现正确的 HTTP 响应吗?
【问题讨论】:
【参考方案1】:这是一个非常古老的问题。但我认为如果我向您介绍一种更简洁的方法来处理我在亲爱的"Jesse Webb's answer" 中看到的 Http 异常,这是值得的。
解决方案是使用system.webServer
部分的httpErrors
元素:
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" subStatusCode="-1" />
<remove statusCode="500" subStatusCode="-1" />
<error statusCode="404" path="/Error/NotFound" responseMode="ExecuteURL" />
<error statusCode="500" path="/Error" responseMode="ExecuteURL" />
</httpErrors>
您也可以通过这种方式记录所有异常。 “阅读"Jesse Webb's answer"”。
这确实感觉更干净,并且与其他所有解决方案一样有效(没有重定向)。
注意:这仅适用于 IIS 7 及更高版本。 (因为最近添加了httpErrors
元素。
【讨论】:
【参考方案2】:对于 HTTP 404 错误(没有重定向),请查看我关于该主题的博客文章。这可能会给你一些好主意:
http://hectorcorrea.com/blog/returning-http-404-in-asp-net-mvc/16
【讨论】:
很好——你正在按照 Darin 的建议去做,尽管他有更多的基础设施来处理多种异常类型。我现在正在努力尝试将他的逻辑从 Application_Error 中移到 ErrorController 本身。 内容不再可用 @Darrren 链接再次可用。【参考方案3】:这是您可以使用的一种技术。定义一个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");
【讨论】:
看起来不错——我会在几个小时内试一试,然后告诉你它是如何工作的。我没有看到的一件事:您在哪里处理找不到 Controller 的情况? @Val,当找不到控制器时,ASP.NET MVC 会自动抛出代码为 404 的 HttpException,因此它将进入第一种情况。 好的,我实现了基础知识,现在我要回过头来为各种 HTTP 错误实现自定义错误视图,并传递相关信息以显示在每个错误中。我有更多的工作来连接它——构建一个错误视图模型,将它填充到 ErrorController 等,但基本功能很好用。没有 302,而且,正如您所评论的,丢失的动作和控制器得到了正确处理。谢谢 - 这是我见过的最简单、最干净的正常工作示例! 一如既往@DarinDimitrov 的回答非常出色!现在我在 Firebug 中得到 404 响应而不是 302。 @Darin:这也可以在 MVC3 中完成,还是有其他方法可以做到?如果有,怎么做?【参考方案4】:这并不能回答您的问题,但重要的是要注意 HTTP 状态 500 表示服务器出现问题,因此您的示例:
POST /User/New/new-user-name-too-long-for-db-column-constraint
不是抛出 500 的有效理由,它是一个数据验证问题,应该由 MVC 数据注释或 jQuery 验证框架等处理。只是在文本框旁边显示一条错误消息,说“用户名太长”是好多了。
【讨论】:
是的,我应该验证并抛出验证错误。但是,如果我在无法插入或更新行时无法验证并从数据库获得约束错误,并且我没有正确检查,我想抛出内部服务器错误。事实上,这就是我发现我们缺少一些验证的原因!以上是关于如何在 .NET MVC 2 中实现正确的 HTTP 错误处理?的主要内容,如果未能解决你的问题,请参考以下文章