在调查了这个问题后,我想分享我的知识。欢迎任何有助于改进我的陈述的评论。
在 ASP.NET MVC 中,有三层按以下顺序处理 HTTP 请求(响应以相反的顺序传输):
IIS(HTTP 层)
ASP.NET(服务器层)
控制器(MVC 层)
所有这些层都有错误处理,但每一层的处理方式不同。我将从 IIS 开始。
IIS 层
IIS 如何处理错误的最简单示例是使用浏览器从您的服务器请求一个不存在的 .html 文件。地址应如下所示:
http://localhost:50123/this_does_not_exist.html
注意浏览器选项卡的标题,例如:IIS 10.0 详细错误 - 404.0 - 未找到。
ASP.NET 层
当 IIS 接收到 HTTP 请求时,如果 URL 以 .aspx 结尾,它会将其转发到 ASP.NET,因为它已注册为处理此扩展。ASP.NET 如何处理错误的最简单示例是使用浏览器从您的服务器请求一个不存在的 .aspx 文件。地址应如下所示:
http://localhost:50123/this_does_not_exist.aspx
请注意页面底部显示的版本信息,指示 ASP.NET 的版本。
customErrors标记最初是为 ASP.NET 创建的。仅当响应由 ASP.NET 内部代码创建时才有效。这意味着它不会影响从应用程序代码创建的响应。另外,如果 ASP.NET 返回的响应没有内容并且有错误状态码(4xx 或 5xx),那么 IIS 会根据状态码替换响应。我将提供一些例子。
如果Page_Load方法包含Response.StatusCode = 404
,则内容正常显示。如果添加了附加代码Response.SuppressContent = true
,则 IIS 会以与请求“this_does_not_exist.html”时相同的方式进行干预和处理 404 错误。没有内容和状态代码 2xx 的 ASP.NET 响应不受影响。
当 ASP.NET 无法使用应用程序代码完成请求时,它将使用内部代码处理它。请参阅以下示例。
如果无法解析 URL,ASP.NET 会自行生成响应。默认情况下,它会创建一个带有 HTML 正文的 404 响应,其中包含有关问题的详细信息。customErrors 可用于创建 302(重定向)响应。但是,访问导致 ASP.NET 返回 404 响应的有效 URL 不会触发 customErrors 指定的重定向。
当 ASP.NET 从应用程序代码中捕获异常时,也会发生同样的情况。默认情况下,它会创建一个带有 HTML 正文的 500 响应,其中包含有关导致异常的源代码的详细信息。同样,customErrors 可用于生成 302(重定向)响应。但是,从应用程序代码创建 500 响应不会触发 customErrors 指定的重定向。
考虑到我刚才所说的,defaultRedirect和错误标签非常容易理解。错误标签用于指定特定状态代码的重定向。如果没有对应的错误标签,则使用 defaultRedirect。重定向 URL 可以指向服务器可以处理的任何内容,包括控制器操作。
MVC 层
使用 ASP.NET MVC,事情变得更加复杂。首先,可能有两个“Web.config”文件,一个在根目录,一个在 Views 文件夹中。我想指出,来自 Views 的默认“Web.config”对此线程有两件感兴趣的事情:
- 它禁用处理 .cshtml 文件的 URL(webpages:Enabled 设置为 false)
- 它可以防止直接访问 Views 文件夹 (BlockViewHandler) 中的任何内容
在 ASP.NET MVC 的情况下,可以将HandleErrorAttribute添加到GlobalFilters中,这也考虑了来自根“Web.config”的 customErrors 标记的mode属性值。更具体地说,当设置为 On 时,它会在 MVC 层针对控制器/操作代码中的未捕获异常启用错误处理。默认情况下,它不会将它们转发到 ASP.NET,而是呈现 Views/Shared/Error.cshtml。这可以通过设置 HandleErrorAttribute 的 View 属性来改变。
MVC 层的错误处理在控制器/动作被解析后开始,基于请求 URL。例如,不满足操作参数的请求在 MVC 层处理。但是,如果 POST 请求没有可以处理 POST 的匹配控制器/操作,则错误将在 ASP.NET 层处理。
我使用 ASP.NET MVC 5 进行测试。IIS 和 IIS Express 在错误处理方面似乎没有区别。
回答
我能想到为什么非 500 状态代码不考虑 customErrors 的唯一原因是因为它们是使用 HttpStatusCodeResponse 创建的。在这种情况下,响应由应用程序代码创建,不由 ASP.NET 处理,而是由 IIS 处理。此时配置替代页面是没有意义的。以下是重现此行为的示例代码:
public ActionResult Unhandled404Error()
{
return new HttpStatusCodeResult(HttpStatusCode.NotFound);
}
在这种情况下,我建议实现一个ActionFilterAttribute,它将覆盖OnResultExecuted
并执行以下操作:
int statusCode = filterContext.HttpContext.Response.StatusCode;
if(statusCode >= 400)
{
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.Redirect("/Home/Index");
}
应将实现的 ActionFilterAttribute 添加到 GlobalFilters。