作者:Tom Dykstra、Luke Latham 和 Steve Smith
本文介绍了处理 ASP.NET Core Web 应用中常见错误的一些方法。 有关 Web API,请参阅 处理 ASP.NET Core Web API 中的错误。
查看或下载示例代码。 (下载方法。)本文介绍了如何在示例应用中设置预处理器指令(#if
、#endif
、#define
)来启用不同方案。
开发人员异常页 显示请求异常的详细信息。 此页是通过 Microsoft.AspNetCore.App 元包中的 Microsoft.AspNetCore.Diagnostics 包提供。 向 Startup.Configure
方法添加代码,以当应用在开发环境中运行时启用此页:
if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); app.UseHsts(); }
将 UseDeveloperExceptionPage 调用置于要捕获其异常的任何中间件前面。
警告
仅当应用程序在开发环境中运行时才启用开发人员异常页 。 否则当应用程序在生产环境中运行时,详细的异常信息会向公众泄露 有关配置环境的详细信息,请参阅 在 ASP.NET Core 中使用多个环境。
该页包括关于异常和请求的以下信息:
若要在示例应用中查看开发人员异常页,请使用 DevEnvironment
预处理器指令,并选择主页上的“触发异常” 。
若要为生产环境配置自定义错误处理页,请使用异常处理中间件。 中间件:
在下面的示例中,UseExceptionHandler 在非开发环境中添加异常处理中间件:
if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); app.UseHsts(); }
Razor Pages 应用模板提供“页面” 文件夹中的 Error 页 (.cshtml ) 和 PageModel 类 (ErrorModel
)。 对于 MVC 应用,项目模板包括 Error 操作方法和 Error 视图。 操作方法如下:
[AllowAnonymous] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); }
不要使用 HTTP 方法属性(如 HttpGet
)标记错误处理程序操作方法。 显式谓词可阻止某些请求访问方法。 允许匿名访问方法,以便未经身份验证的用户能够接收错误视图。
使用 IExceptionHandlerPathFeature 访问错误处理程序控制器或页中的异常和原始请求路径:
var exceptionHandlerPathFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>(); if (exceptionHandlerPathFeature?.Error is FileNotFoundException) { ExceptionMessage = "File error thrown"; } if (exceptionHandlerPathFeature?.Path == "/index") { ExceptionMessage += " from home page"; }
警告
请勿 向客户端提供敏感错误信息。 提供服务的错误是一种安全风险。
若要在示例应用中查看异常处理页,请使用 ProdEnvironment
和 ErrorHandlerPage
预处理器指令,并选择主页上的“触发异常” 。
自定义异常处理程序页的替代方法是向 UseExceptionHandler 提供 lambda。 使用 lambda,可以在返回响应前访问错误。
下面的示例展示了如何使用 lambda 进行异常处理:
if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler(errorApp => { errorApp.Run(async context => { context.Response.StatusCode = 500; context.Response.ContentType = "text/html"; await context.Response.WriteAsync("<html lang=\"en\"><body>\r\n"); await context.Response.WriteAsync("ERROR!<br><br>\r\n"); var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>(); // Use exceptionHandlerPathFeature to process the exception (for example, // logging), but do NOT expose sensitive error information directly to // the client. if (exceptionHandlerPathFeature?.Error is FileNotFoundException) { await context.Response.WriteAsync("File error thrown!<br><br>\r\n"); } await context.Response.WriteAsync("<a href=\"/\">Home</a><br>\r\n"); await context.Response.WriteAsync("</body></html>\r\n"); await context.Response.WriteAsync(new string(' ', 512)); // IE padding }); }); app.UseHsts(); }
在前面的代码中,添加了 await context.Response.WriteAsync(new string(' ', 512));
,以便 Internet Explorer 浏览器显示相应的错误消息,而非显示 IE 错误消息。 有关详细信息,请参阅此 GitHub 问题。
警告
不要 向客户端提供来自 IExceptionHandlerFeature 或 IExceptionHandlerPathFeature 的敏感错误信息。 提供服务的错误是一种安全风险。
若要在示例应用中查看异常处理 lambda 的结果,请使用 ProdEnvironment
和 ErrorHandlerLambda
预处理器指令,并选择主页上的“触发异常” 。
默认情况下,ASP.NET Core 应用不会为 HTTP 状态代码(如“404 - 未找到” )提供状态代码页。 应用返回状态代码和空响应正文。 若要提供状态代码页,请使用状态代码页中间件。
此中间件是通过 Microsoft.AspNetCore.App 元包中的 Microsoft.AspNetCore.Diagnostics 包提供。
若要启用常见错误状态代码的默认纯文本处理程序,请在 Startup.Configure
方法中调用 UseStatusCodePages:
app.UseStatusCodePages();
在请求处理中间件(例如,静态文件中间件和 MVC 中间件)前面调用 UseStatusCodePages
。
下面的示例展示了默认处理程序显示的文本:
Status Code: 404; Not Found
若要在示例应用中查看各种状态代码页格式之一,请使用以 StatusCodePages
开头的预处理器指令之一,并选择主页上的“触发 404” 。
若要自定义响应内容类型和文本,请利用需要使用内容类型和格式字符串的 UseStatusCodePages 重载:
app.UseStatusCodePages( "text/plain", "Status code page, status code: {0}");
若要指定自定义错误处理和响应写入代码,请利用需要使用 lambda 表达式的 UseStatusCodePages 重载:
app.UseStatusCodePages(async context => { context.HttpContext.Response.ContentType = "text/plain"; await context.HttpContext.Response.WriteAsync( "Status code page, status code: " + context.HttpContext.Response.StatusCode); });
UseStatusCodePagesWithRedirects 扩展方法:
app.UseStatusCodePagesWithRedirects("/StatusCode?code={0}");
URL 模板可能会包括状态代码的 {0}
占位符,如上面的示例所示。 如果 URL 模板以波形符 (~) 开头,波形符会替换为应用的 PathBase
。 如果在应用中指向终结点,请为终结点创建 MVC 视图或 Razor 页面。 有关 Razor Pages 示例,请参阅示例应用中的 Pages/StatusCode.cshtml 。
使用此方法通常是当应用:
UseStatusCodePagesWithReExecute 扩展方法:
app.UseStatusCodePagesWithReExecute("/StatusCode","?code={0}");
如果在应用中指向终结点,请为终结点创建 MVC 视图或 Razor 页面。 有关 Razor Pages 示例,请参阅示例应用中的 Pages/StatusCode.cshtml 。
使用此方法通常是当应用应:
URL 模板和查询字符串模板可能包括状态代码的占位符 ({0}
)。 URL 模板必须以斜杠 (/
) 开头。 若要在路径中使用占位符,请确认终结点(页或控制器)能否处理路径段。 例如,错误的 Razor Page 应通过 @page
指令接受可选路径段值:
@page "{code?}"
错误处理终结点可以获取生成错误的原始 URL,如下面的示例所示:
var statusCodeReExecuteFeature = HttpContext.Features.Get<IStatusCodeReExecuteFeature>(); if (statusCodeReExecuteFeature != null) { OriginalURL = statusCodeReExecuteFeature.OriginalPathBase + statusCodeReExecuteFeature.OriginalPath + statusCodeReExecuteFeature.OriginalQueryString; }
若要禁用 MVC 控制器或操作方法的状态代码页,请使用 [SkipStatusCodePages]
特性。
若要禁用 Razor Pages 处理程序方法或 MVC 控制器中的特定请求的状态代码页,请使用 IStatusCodePagesFeature:
var statusCodePagesFeature = HttpContext.Features.Get<IStatusCodePagesFeature>(); if (statusCodePagesFeature != null) { statusCodePagesFeature.Enabled = false; }
异常处理页中的代码可能会引发异常。 建议在生产错误页面中包含纯静态内容。
在响应头发送后:
除了应用中的异常处理逻辑外,HTTP 服务器实现还能处理一些异常。 如果服务器在发送响应标头之前捕获到异常,服务器将发送不包含响应正文的“500 - 内部服务器错误” 响应。 如果服务器在发送响应标头后捕获到异常,服务器会关闭连接。 应用程序无法处理的请求将由服务器进行处理。 当服务器处理请求时,发生的任何异常都将由服务器的异常处理进行处理。 应用的自定义错误页面、异常处理中间件和筛选器都不会影响此行为。
应用程序启动期间发生的异常仅可在承载层进行处理。 可以将主机配置为,捕获启动错误和捕获详细错误。
仅当错误在主机地址/端口绑定后出现时,托管层才能显示捕获的启动错误的错误页。 如果绑定失败:
在 IIS(或 Azure 应用服务)或 IIS Express 上运行应用时,如果无法启动进程,ASP.NET Core 模块将返回“502.5 - 进程失败” 。 有关详细信息,请参阅 Azure App Service 和 IIS 上的 ASP.NET Core 疑难解答。
数据库错误页中间件捕获与数据库相关的异常,可使用实体框架迁移来解析这些异常。 当这些异常出现时,便会生成 HTML 响应,其中包含用于解决问题的可能操作的详细信息。 应仅在开发环境中启用此页。 通过向 Startup.Configure
添加代码来启用此页:
if (env.IsDevelopment()) { app.UseDatabaseErrorPage(); }
在 MVC 应用中,可以全局配置异常筛选器,也可以为每个控制器或每个操作单独配置。 在 Razor Pages 应用中,可以全局配置异常筛选器,也可以为每个页面模型单独配置。 这些筛选器处理在执行控制器操作或其他筛选器时出现的任何未处理的异常。 有关详细信息,请参阅 ASP.NET Core 中的筛选器。
提示
异常筛选器适合捕获 MVC 操作内发生的异常,但它们不如异常处理中间件灵活。 建议使用中间件。 仅在需要根据选定 MVC 操作以不同方式执行错误处理时,才使用筛选器。