处理 ASP.NET Core 中的错误Handle errors in ASP.NET Core

作者:Tom DykstraLuke LathamSteve SmithBy Tom Dykstra, Luke Latham, and Steve Smith

本文介绍了处理 ASP.NET Core Web 应用中常见错误的一些方法。This article covers common approaches to handling errors in ASP.NET Core web apps. 有关 Web API,请参阅 处理 ASP.NET Core Web API 中的错误See 处理 ASP.NET Core Web API 中的错误 for web APIs.

查看或下载示例代码View or download sample code. 下载方法。)本文介绍了如何在示例应用中设置预处理器指令(#if#endif#define)来启用不同方案。(How to download.) The article includes instructions about how to set preprocessor directives (#if, #endif, #define) in the sample app to enable different scenarios.

开发人员异常页Developer Exception Page

开发人员异常页 显示请求异常的详细信息。The Developer Exception Page displays detailed information about request exceptions. 此页是通过 Microsoft.AspNetCore.App 元包中的 Microsoft.AspNetCore.Diagnostics 包提供。The page is made available by the Microsoft.AspNetCore.Diagnostics package, which is in the Microsoft.AspNetCore.App metapackage. Startup.Configure 方法添加代码,以当应用在开发环境中运行时启用此页:Add code to the Startup.Configure method to enable the page when the app is running in the Development environment:

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

UseDeveloperExceptionPage 调用置于要捕获其异常的任何中间件前面。Place the call to UseDeveloperExceptionPage before any middleware that you want to catch exceptions.

警告

仅当应用程序在开发环境中运行时才启用开发人员异常页 。Enable the Developer Exception Page only when the app is running in the Development environment. 否则当应用程序在生产环境中运行时,详细的异常信息会向公众泄露You don't want to share detailed exception information publicly when the app runs in production. 有关配置环境的详细信息,请参阅 在 ASP.NET Core 中使用多个环境For more information on configuring environments, see 在 ASP.NET Core 中使用多个环境.

该页包括关于异常和请求的以下信息:The page includes the following information about the exception and the request:

  • 堆栈跟踪Stack trace
  • 查询字符串参数(如果有)Query string parameters (if any)
  • Cookie(如果有)Cookies (if any)
  • 标头Headers

若要在示例应用中查看开发人员异常页,请使用 DevEnvironment 预处理器指令,并选择主页上的“触发异常” 。To see the Developer Exception Page in the sample app, use the DevEnvironment preprocessor directive and select Trigger an exception on the home page.

异常处理程序页Exception handler page

若要为生产环境配置自定义错误处理页,请使用异常处理中间件。To configure a custom error handling page for the Production environment, use the Exception Handling Middleware. 中间件:The middleware:

  • 捕获并记录异常。Catches and logs exceptions.
  • 在备用管道中为指定的页或控制器重新执行请求。Re-executes the request in an alternate pipeline for the page or controller indicated. 如果响应已启动,则不会重新执行请求。The request isn't re-executed if the response has started.

在下面的示例中,UseExceptionHandler 在非开发环境中添加异常处理中间件:In the following example, UseExceptionHandler adds the Exception Handling Middleware in non-Development environments:

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

Razor Pages 应用模板提供“页面” 文件夹中的 Error 页 (.cshtml ) 和 PageModel 类 (ErrorModel)。The Razor Pages app template provides an Error page (.cshtml) and PageModel class (ErrorModel) in the Pages folder. 对于 MVC 应用,项目模板包括 Error 操作方法和 Error 视图。For an MVC app, the project template includes an Error action method and an Error view. 操作方法如下:Here's the action method:

[AllowAnonymous]
public IActionResult Error()
{
    return View(new ErrorViewModel 
        { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}

不要使用 HTTP 方法属性(如 HttpGet)修饰错误处理程序操作方法。Don't decorate the error handler action method with HTTP method attributes, such as HttpGet. 显式谓词可阻止某些请求访问方法。Explicit verbs prevent some requests from reaching the method. 允许匿名访问方法,以便未经身份验证的用户能够接收错误视图。Allow anonymous access to the method so that unauthenticated users are able to receive the error view.

访问异常Access the exception

使用 IExceptionHandlerPathFeature 访问错误处理程序控制器或页中的异常和原始请求路径:Use IExceptionHandlerPathFeature to access the exception and the original request path in an error handler controller or page:

var exceptionHandlerPathFeature =
    HttpContext.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
    ExceptionMessage = "File error thrown";
}
if (exceptionHandlerPathFeature?.Path == "/index")
{
    ExceptionMessage += " from home page";
}

警告

请勿 向客户端提供敏感错误信息。Do not serve sensitive error information to clients. 提供服务的错误是一种安全风险。Serving errors is a security risk.

若要在示例应用中查看异常处理页,请使用 ProdEnvironmentErrorHandlerPage 预处理器指令,并选择主页上的“触发异常” 。To see the exception handling page in the sample app, use the ProdEnvironment and ErrorHandlerPage preprocessor directives, and select Trigger an exception on the home page.

异常处理程序 lambdaException handler lambda

自定义异常处理程序页的替代方法是向 UseExceptionHandler 提供 lambda。An alternative to a custom exception handler page is to provide a lambda to UseExceptionHandler. 使用 lambda,可以在返回响应前访问错误。Using a lambda allows access to the error before returning the response.

下面的示例展示了如何使用 lambda 进行异常处理:Here's an example of using a lambda for exception handling:

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();
}

警告

不要 向客户端提供来自 IExceptionHandlerFeatureIExceptionHandlerPathFeature 的敏感错误信息。Do not serve sensitive error information from IExceptionHandlerFeature or IExceptionHandlerPathFeature to clients. 提供服务的错误是一种安全风险。Serving errors is a security risk.

若要在示例应用中查看异常处理 lambda 的结果,请使用 ProdEnvironmentErrorHandlerLambda 预处理器指令,并选择主页上的“触发异常” 。To see the result of the exception handling lambda in the sample app, use the ProdEnvironment and ErrorHandlerLambda preprocessor directives, and select Trigger an exception on the home page.

UseStatusCodePagesUseStatusCodePages

默认情况下,ASP.NET Core 应用不会为 HTTP 状态代码(如“404 - 未找到” )提供状态代码页。By default, an ASP.NET Core app doesn't provide a status code page for HTTP status codes, such as 404 - Not Found. 应用返回状态代码和空响应正文。The app returns a status code and an empty response body. 若要提供状态代码页,请使用状态代码页中间件。To provide status code pages, use Status Code Pages middleware.

此中间件是通过 Microsoft.AspNetCore.App 元包中的 Microsoft.AspNetCore.Diagnostics 包提供。The middleware is made available by the Microsoft.AspNetCore.Diagnostics package, which is in the Microsoft.AspNetCore.App metapackage.

若要启用常见错误状态代码的默认纯文本处理程序,请在 Startup.Configure 方法中调用 UseStatusCodePagesTo enable default text-only handlers for common error status codes, call UseStatusCodePages in the Startup.Configure method:

app.UseStatusCodePages();

在请求处理中间件(例如,静态文件中间件和 MVC 中间件)前面调用 UseStatusCodePagesCall UseStatusCodePages before request handling middleware (for example, Static File Middleware and MVC Middleware).

下面的示例展示了默认处理程序显示的文本:Here's an example of text displayed by the default handlers:

Status Code: 404; Not Found

若要在示例应用中查看各种状态代码页格式之一,请使用以 StatusCodePages 开头的预处理器指令之一,并选择主页上的“触发 404” 。To see one of the various status code page formats in the sample app, use one of the preprocessor directives that begin with StatusCodePages, and select Trigger a 404 on the home page.

包含格式字符串的 UseStatusCodePagesUseStatusCodePages with format string

若要自定义响应内容类型和文本,请利用需要使用内容类型和格式字符串的 UseStatusCodePages 重载:To customize the response content type and text, use the overload of UseStatusCodePages that takes a content type and format string:

app.UseStatusCodePages(
    "text/plain", "Status code page, status code: {0}");            

包含 lambda 的 UseStatusCodePagesUseStatusCodePages with lambda

若要指定自定义错误处理和响应写入代码,请利用需要使用 lambda 表达式的 UseStatusCodePages 重载:To specify custom error-handling and response-writing code, use the overload of UseStatusCodePages that takes a lambda expression:


app.UseStatusCodePages(async context =>
{
    context.HttpContext.Response.ContentType = "text/plain";

    await context.HttpContext.Response.WriteAsync(
        "Status code page, status code: " + 
        context.HttpContext.Response.StatusCode);
});

UseStatusCodePagesWithRedirectsUseStatusCodePagesWithRedirects

UseStatusCodePagesWithRedirects 扩展方法:The UseStatusCodePagesWithRedirects extension method:

  • 向客户端发送“302 - 已找到” 状态代码。Sends a 302 - Found status code to the client.
  • 将客户端重定向到 URL 模板中的位置。Redirects the client to the location provided in the URL template.
app.UseStatusCodePagesWithRedirects("/StatusCode?code={0}");

URL 模板可能会包括状态代码的 {0} 占位符,如上面的示例所示。The URL template can include a {0} placeholder for the status code, as shown in the example. 如果 URL 模板以波形符 (~) 开头,波形符会替换为应用的 PathBaseIf the URL template starts with a tilde (~), the tilde is replaced by the app's PathBase. 如果在应用中指向终结点,请为终结点创建 MVC 视图或 Razor 页面。If you point to an endpoint within the app, create an MVC view or Razor page for the endpoint. 有关 Razor Pages 示例,请参阅示例应用中的 Pages/StatusCode.cshtml 。For a Razor Pages example, see Pages/StatusCode.cshtml in the sample app.

使用此方法通常是当应用:This method is commonly used when the app:

  • 应将客户端重定向到不同的终结点(通常在不同的应用处理错误的情况下)。Should redirect the client to a different endpoint, usually in cases where a different app processes the error. 对于 Web 应用,客户端的浏览器地址栏反映重定向终结点。For web apps, the client's browser address bar reflects the redirected endpoint.
  • 不应保留原始状态代码并通过初始重定向响应返回该代码。Shouldn't preserve and return the original status code with the initial redirect response.

UseStatusCodePagesWithReExecuteUseStatusCodePagesWithReExecute

UseStatusCodePagesWithReExecute 扩展方法:The UseStatusCodePagesWithReExecute extension method:

  • 向客户端返回原始状态代码。Returns the original status code to the client.
  • 通过使用备用路径重新执行请求管道,从而生成响应正文。Generates the response body by re-executing the request pipeline using an alternate path.
app.UseStatusCodePagesWithReExecute("/StatusCode","?code={0}");

如果在应用中指向终结点,请为终结点创建 MVC 视图或 Razor 页面。If you point to an endpoint within the app, create an MVC view or Razor page for the endpoint. 有关 Razor Pages 示例,请参阅示例应用中的 Pages/StatusCode.cshtml 。For a Razor Pages example, see Pages/StatusCode.cshtml in the sample app.

使用此方法通常是当应用应:This method is commonly used when the app should:

  • 处理请求,但不重定向到不同终结点。Process the request without redirecting to a different endpoint. 对于 Web 应用,客户端的浏览器地址栏反映最初请求的终结点。For web apps, the client's browser address bar reflects the originally requested endpoint.
  • 保留原始状态代码并通过响应返回该代码。Preserve and return the original status code with the response.

URL 模板和查询字符串模板可能包括状态代码的占位符 ({0})。The URL and query string templates may include a placeholder ({0}) for the status code. URL 模板必须以斜杠 (/) 开头。The URL template must start with a slash (/). 若要在路径中使用占位符,请确认终结点(页或控制器)能否处理路径段。When using a placeholder in the path, confirm that the endpoint (page or controller) can process the path segment. 例如,错误的 Razor Page 应通过 @page 指令接受可选路径段值:For example, a Razor Page for errors should accept the optional path segment value with the @page directive:

@page "{code?}"

错误处理终结点可以获取生成错误的原始 URL,如下面的示例所示:The endpoint that processes the error can get the original URL that generated the error, as shown in the following example:

var statusCodeReExecuteFeature = HttpContext.Features.Get<IStatusCodeReExecuteFeature>();
if (statusCodeReExecuteFeature != null)
{
    OriginalURL =
        statusCodeReExecuteFeature.OriginalPathBase
        + statusCodeReExecuteFeature.OriginalPath
        + statusCodeReExecuteFeature.OriginalQueryString;
}

禁用状态代码页Disable status code pages

若要禁用 MVC 控制器或操作方法的状态代码页,请使用 [SkipStatusCodePages] 特性。To disable status code pages for an MVC controller or action method, use the [SkipStatusCodePages] attribute.

若要禁用 Razor Pages 处理程序方法或 MVC 控制器中的特定请求的状态代码页,请使用 IStatusCodePagesFeatureTo disable status code pages for specific requests in a Razor Pages handler method or in an MVC controller, use IStatusCodePagesFeature:

var statusCodePagesFeature = HttpContext.Features.Get<IStatusCodePagesFeature>();

if (statusCodePagesFeature != null)
{
    statusCodePagesFeature.Enabled = false;
}

异常处理代码Exception-handling code

异常处理页中的代码可能会引发异常。Code in exception handling pages can throw exceptions. 建议在生产错误页面中包含纯静态内容。It's often a good idea for production error pages to consist of purely static content.

响应头Response headers

在响应头发送后:Once the headers for a response are sent:

  • 应用无法更改响应的状态代码。The app can't change the response's status code.
  • 任何异常页或处理程序都无法运行。Any exception pages or handlers can't run. 必须完成响应或中止连接。The response must be completed or the connection aborted.

服务器异常处理Server exception handling

除了应用中的异常处理逻辑外,HTTP 服务器实现还能处理一些异常。In addition to the exception handling logic in your app, the HTTP server implementation can handle some exceptions. 如果服务器在发送响应标头之前捕获到异常,服务器将发送不包含响应正文的“500 - 内部服务器错误” 响应。If the server catches an exception before response headers are sent, the server sends a 500 - Internal Server Error response without a response body. 如果服务器在发送响应标头后捕获到异常,服务器会关闭连接。If the server catches an exception after response headers are sent, the server closes the connection. 应用程序无法处理的请求将由服务器进行处理。Requests that aren't handled by your app are handled by the server. 当服务器处理请求时,发生的任何异常都将由服务器的异常处理进行处理。Any exception that occurs when the server is handling the request is handled by the server's exception handling. 应用的自定义错误页面、异常处理中间件和筛选器都不会影响此行为。The app's custom error pages, exception handling middleware, and filters don't affect this behavior.

启动异常处理Startup exception handling

应用程序启动期间发生的异常仅可在承载层进行处理。Only the hosting layer can handle exceptions that take place during app startup. 可以将主机配置为,捕获启动错误捕获详细错误The host can be configured to capture startup errors and capture detailed errors.

仅当错误在主机地址/端口绑定后出现时,托管层才能显示捕获的启动错误的错误页。The hosting layer can show an error page for a captured startup error only if the error occurs after host address/port binding. 如果绑定失败:If binding fails:

  • 托管层将记录关键异常。The hosting layer logs a critical exception.
  • dotnet 进程崩溃。The dotnet process crashes.
  • 不会在 HTTP 服务器为 Kestrel 时显示任何错误页。No error page is displayed when the HTTP server is Kestrel.

IIS(或 Azure 应用服务)或 IIS Express 上运行应用时,如果无法启动进程,ASP.NET Core 模块将返回“502.5 - 进程失败” 。When running on IIS (or Azure App Service) or IIS Express, a 502.5 - Process Failure is returned by the ASP.NET Core Module if the process can't start. 有关详细信息,请参阅 Azure App Service 和 IIS 上的 ASP.NET Core 疑难解答For more information, see Azure App Service 和 IIS 上的 ASP.NET Core 疑难解答.

数据库错误页Database error page

数据库错误页中间件捕获与数据库相关的异常,可使用实体框架迁移来解析这些异常。Database Error Page Middleware captures database-related exceptions that can be resolved by using Entity Framework migrations. 当这些异常出现时,便会生成 HTML 响应,其中包含用于解决问题的可能操作的详细信息。When these exceptions occur, an HTML response with details of possible actions to resolve the issue is generated. 应仅在开发环境中启用此页。This page should be enabled only in the Development environment. 通过向 Startup.Configure 添加代码来启用此页:Enable the page by adding code to Startup.Configure:

if (env.IsDevelopment())
{
    app.UseDatabaseErrorPage();
}

异常筛选器Exception filters

在 MVC 应用中,可以全局配置异常筛选器,也可以为每个控制器或每个操作单独配置。In MVC apps, exception filters can be configured globally or on a per-controller or per-action basis. 在 Razor Pages 应用中,可以全局配置异常筛选器,也可以为每个页面模型单独配置。In Razor Pages apps, they can be configured globally or per page model. 这些筛选器处理在执行控制器操作或其他筛选器时出现的任何未处理的异常。These filters handle any unhandled exception that occurs during the execution of a controller action or another filter. 有关详细信息,请参阅 ASP.NET Core 中的筛选器For more information, see ASP.NET Core 中的筛选器.

提示

异常筛选器适合捕获 MVC 操作内发生的异常,但它们不如异常处理中间件灵活。Exception filters are useful for trapping exceptions that occur within MVC actions, but they're not as flexible as the Exception Handling Middleware. 建议使用中间件。We recommend using the middleware. 仅在需要根据选定 MVC 操作以不同方式执行错误处理时,才使用筛选器。Use filters only where you need to perform error handling differently based on which MVC action is chosen.

模型状态错误Model state errors

若要了解如何处理模型状态错误,请参阅模型绑定模型验证For information about how to handle model state errors, see Model binding and Model validation.

其他资源Additional resources