如何在.NET MVC 2中实现正确的HTTP错误处理?

时间:2011-02-06 02:54:21

标签: asp.net-mvc asp.net-mvc-2 http error-handling httpresponse

我一直在努力在我的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:

  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
  5. 这是我认为的500:

    1. 发布错误值:POST /User/New/new-user-name-too-long-for-db-column-constraint
    2. 与数据无关的问题,如无响应的Web服务端点
    3. 其中一些问题需要由特定的控制器或模型识别,然后控制器应抛出适当的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响应的全面详细信息吗?

4 个答案:

答案 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元素。