ASP.NET MVC 全局异常处理与自定义错误页实现
在 ASP.NET MVC 应用中,统一捕获并展示用户友好的错误页面是提升健壮性和用户体验的关键环节。以下是一种基于全局事件与控制器路由的轻量级实现方案。
1. 利用 Application_Error 实现异常拦截
在 Global.asax.cs 的 Application_Error 方法中,可对未处理异常进行分类响应:
protected void Application_Error(object sender, EventArgs e)
{
var lastEx = Server.GetLastError();
if (lastEx == null) return;
var httpEx = lastEx as HttpException;
int statusCode = httpEx?.GetHttpCode() ?? 500;
// 区分客户端错误与服务器内部错误
switch (statusCode)
{
case 400:
case 404:
Response.Clear();
Server.ClearError();
Response.StatusCode = 404;
Response.RedirectToRoute("ErrorRoute", new { code = "NotFound" });
break;
default:
// 记录原始请求体(仅限调试,生产环境建议异步日志)
string rawBody = string.Empty;
try
{
Request.InputStream.Seek(0, System.IO.SeekOrigin.Begin);
using var reader = new System.IO.StreamReader(Request.InputStream, Request.ContentEncoding);
rawBody = reader.ReadToEnd();
}
catch { /* 忽略流读取异常 */ }
// 写入诊断日志(如 NLog、Serilog 或自定义日志器)
// Logger.Error(lastEx, "Uncaught server error. Raw body: {RawBody}", rawBody.Substring(0, Math.Min(512, rawBody.Length)));
Response.Clear();
Server.ClearError();
Response.StatusCode = 500;
Response.RedirectToRoute("ErrorRoute", new { code = "ServerError" });
break;
}
}
2. 创建专用错误处理控制器
新建 ErrorController,支持按错误类型动态渲染视图:
public class ErrorController : Controller
{
public ActionResult Display(string code)
{
ViewBag.ErrorType = code;
ViewBag.Timestamp = DateTime.UtcNow.ToString("o");
// 可选:根据 code 返回不同视图(如 NotFound.cshtml / ServerError.cshtml)
return View("Display", (object)code);
}
}
3. 配置专用路由规则
在 RouteConfig.cs 中注册显式错误路由,确保优先匹配,避免被默认路由覆盖:
routes.MapRoute(
name: "ErrorRoute",
url: "error/{code}",
defaults: new { controller = "Error", action = "Display", code = UrlParameter.Optional },
constraints: new { code = @"^(NotFound|ServerError)$" }
);
// 确保该路由在 Default 路由之前注册
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
4. 关闭 IIS/ASP.NET 默认错误页干预
在根目录 Web.config 的 <system.web> 节点下,禁用 customErrors,防止其劫持状态码:
<system.web>
<customErrors mode="Off" />
</system.web>
同时,在 <system.webServer> 中确保 httpErrors 也设为 disabled(IIS 7+):
<system.webServer>
<httpErrors existingResponse="PassThrough" />
</system.webServer>
5. 移除内置异常过滤器
注释或删除 FilterConfig.RegisterGlobalFilters 中的 HandleErrorAttribute,避免与手动异常处理逻辑冲突:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
// filters.Add(new HandleErrorAttribute()); // ← 禁用此行
}