我一直在努力在我的ASP.NET MVC 2应用程序中实现错误处理。我看过各种各样的技术,但都没有正常工作。我正在使用MVC2和.NET 4.0(在MVC3发布之前启动项目;我们将在发布初始版本后进行升级)。
此时,我将很乐意妥善处理404和500错误 - 403(需要授权)也会很好,其次是各种其他特定的回复。现在,我得到所有404,全部500,全部302s在404之前,或者全部302s在500之前。
以下是我的要求(应该非常接近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
其中一些问题需要由特定的控制器或模型识别,然后控制器应抛出适当的HttpException。其余的应该更一般地处理。
对于404情况#2,如果找不到控制器,我尝试使用自定义ControllerFactory来抛出404。
对于404案例#3,我尝试使用自定义基本控制器来覆盖HandleUnknownAction
并抛出404.
在这两种情况下,我在404之前得到302.而且,我从未得到500错误;如果我修改Web.config以在我的Web服务端点中输入拼写错误,我仍然得到302,然后404说明使用 Web服务的URL(控制器/操作)无法找到。
我还将请求的URL作为(不需要的)查询字符串参数获取:/Error/NotFound?aspxerrorpath=/Home/non-existent-action
这两种技术都来自http://www.niksmit.com/wp/?p=17(如何使用ASP.Net MVC获取正常的404(找不到页面)错误页面),从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;如果我将其更改为{{1}一切看起来像404)。
以下是我已阅读并尝试实施的其他一些内容:
..以及一堆StackOverflow帖子。
对我而言,这种错误处理对于Web应用程序来说是非常基本的,并且MVC框架应该具有开箱即用的默认设置,并且让人们将其扩展为其他工作。也许他们会在将来的版本中做到这一点。与此同时,有人可以向我提供有关如何实施正确HTTP响应的全面详细信息吗?
答案 0 :(得分:48)
这是您可以使用的一种技术。定义将为错误页面提供服务的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 :(得分:4)
对于HTTP 404错误(没有重定向),请查看我关于该主题的博客文章。这可能会给你一些好主意:
http://hectorcorrea.com/blog/returning-http-404-in-asp-net-mvc/16
答案 2 :(得分:0)
这不能解答您的问题,但重要的是要注意HTTP状态500表示服务器上出现了问题,因此您的示例:
POST /User/New/new-user-name-too-long-for-db-column-constraint
抛出500是无效的理由,它是一个数据验证问题,应该由MVC数据注释或jQuery验证框架等处理。只是在TextBox旁边显示错误消息“用户名太长”是好多了。
答案 3 :(得分:0)
这是一个非常古老的问题。但我认为如果我向你介绍一种更清晰的方式处理我亲爱的"Jesse Webb's answer"中的Http Exceptions,那是值得的。
解决方案是使用httpErrors
部分的system.webServer
元素:
<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
元素。