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

作者:Kirk LarkinTom DykstraSteve SmithBy Kirk Larkin, Tom Dykstra, 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. 下载方法。)在测试示例应用时,F12 浏览器开发人员工具上的网络选项卡非常有用。(How to download.) The network tab on the F12 browser developer tools is useful when testing the sample app.

开发人员异常页Developer Exception Page

开发人员异常页显示请求异常的详细信息。The Developer Exception Page displays detailed information about request exceptions. ASP.NET Core 模板会生成以下代码:The ASP.NET Core templates generate the following code:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

当应用在开发环境中运行时,前面突出显示的代码启用开发人员异常页。The preceding highlighted code enables the developer exception page when the app is running in the Development environment.

模板在中间件管道的前面部分放置 UseDeveloperExceptionPage,以便它可以捕获在后面的中间件中引发的异常。The templates place UseDeveloperExceptionPage early in the middleware pipeline so that it can catch exceptions thrown in middleware that follows.

仅*_当应用在开发环境中运行时,前面的代码才启用开发人员异常页。The preceding code enables the Developer Exception Page *only _ when the app runs in the Development environment. 当应用在生产环境中运行时,不应公开显示详细的异常信息。Detailed exception information should not be displayed publicly when the app runs in the Production environment. 有关配置环境的详细信息,请参阅 在 ASP.NET Core 中使用多个环境For more information on configuring environments, see 在 ASP.NET Core 中使用多个环境.

开发人员异常页包括关于异常和请求的以下信息:The Developer Exception Page includes the following information about the exception and the request:

_ 堆栈跟踪_ Stack trace

  • 查询字符串参数(如果有)Query string parameters if any
  • Cookie(如果有)Cookies if any
  • 标头Headers

异常处理程序页Exception handler page

若要为生产环境配置自定义错误处理页,请调用 UseExceptionHandlerTo configure a custom error handling page for the Production environment, call UseExceptionHandler. 此异常处理中间件:This exception handling middleware:

  • 捕获并记录异常。Catches and logs exceptions.
  • 使用指示的路径在备用管道中重新执行请求。Re-executes the request in an alternate pipeline using the path indicated. 如果响应已启动,则不会重新执行请求。The request isn't re-executed if the response has started. 模板生成的代码使用 /Error 路径重新执行请求。The template generated code re-executes the request using the /Error path.

在下面的示例中,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 应用模板在 Pages 文件夹中提供了一个“错误”页面 (.cshtml) 和 PageModel 类 (ErrorModel) 。The Razor Pages app template provides an Error page (.cshtml) and PageModel class (ErrorModel) in the Pages folder. 对于 MVC 应用,项目模板包括 Error 操作方法和主控制器的错误视图。For an MVC app, the project template includes an Error action method and an Error view for the Home controller.

不要使用 HTTP 方法属性(如 HttpGet)标记错误处理程序操作方法。Don't mark the error handler action method with HTTP method attributes, such as HttpGet. 显式谓词可阻止某些请求访问操作方法。Explicit verbs prevent some requests from reaching the action method. 如果未经身份验证的用户应看到错误视图,则允许匿名访问该方法。Allow anonymous access to the method if unauthenticated users should see the error view.

访问异常Access the exception

使用 IExceptionHandlerPathFeature 访问错误处理程序中的异常和原始请求路径。Use IExceptionHandlerPathFeature to access the exception and the original request path in an error handler. 以下代码将 ExceptionMessage 添加到由 ASP.NET Core 模板生成的默认 Pages/Error.cshtml.cs:The following code adds ExceptionMessage to the default Pages/Error.cshtml.cs generated by the ASP.NET Core templates:

[ResponseCache(Duration=0, Location=ResponseCacheLocation.None, NoStore=true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
    public string RequestId { get; set; }
    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
    public string ExceptionMessage { get; set; }
    private readonly ILogger<ErrorModel> _logger;

    public ErrorModel(ILogger<ErrorModel> logger)
    {
        _logger = logger;
    }

    public void OnGet()
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;

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

警告

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

若要在示例应用中测试异常,请执行以下操作:To test the exception in the sample app:

  • 将环境设置为生产环境。Set the environment to production.
  • 从 Program.cs 中的 webBuilder.UseStartup<Startup>(); 删除注释。Remove the comments from webBuilder.UseStartup<Startup>(); in Program.cs.
  • 在主页上选择“触发异常”。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 处理异常:The following code uses a lambda for exception handling:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    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>();

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

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

警告

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

若要在示例应用中测试异常处理 lambda:To test the exception handling lambda in the sample app:

  • 将环境设置为生产环境。Set the environment to production.
  • 从 Program.cs 中的 webBuilder.UseStartup<StartupLambda>(); 删除注释。Remove the comments from webBuilder.UseStartup<StartupLambda>(); in Program.cs.
  • 在主页上选择“触发异常”。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 error status codes, such as 404 - Not Found. 当应用遇到没有正文的 HTTP 400-599 错误状态代码时,它将返回状态代码和空响应正文。When the app encounters an HTTP 400-599 error status code that doesn't have a body, it returns the status code and an empty response body. 若要提供状态代码页,请使用状态代码页中间件。To provide status code pages, use the status code pages middleware. 若要启用常见错误状态代码的默认纯文本处理程序,请在 Startup.Configure 方法中调用 UseStatusCodePagesTo enable default text-only handlers for common error status codes, call UseStatusCodePages in the Startup.Configure method:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseStatusCodePages();

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

在请求处理中间件之前调用 UseStatusCodePagesCall UseStatusCodePages before request handling middleware. 例如,在静态文件中间件和端点中间件之前调用 UseStatusCodePagesFor example, call UseStatusCodePages before the Static File Middleware and the Endpoints Middleware.

未使用 UseStatusCodePages 时,导航到没有终结点的 URL 会返回一条与浏览器相关的错误消息,指示找不到终结点。When UseStatusCodePages isn't used, navigating to a URL without an endpoint returns a browser dependent error message indicating the endpoint can't be found. 例如,导航到 Home/Privacy2For example, navigating to Home/Privacy2. 调用 UseStatusCodePages 时,浏览器返回:When UseStatusCodePages is called, the browser returns:

Status Code: 404; Not Found

UseStatusCodePages 通常不在生产中使用,因为它返回对用户没有用的消息。UseStatusCodePages isn't typically used in production because it returns a message that isn't useful to users.

若要在示例应用中测试 UseStatusCodePagesTo test UseStatusCodePages in the sample app:

  • 将环境设置为生产环境。Set the environment to production.
  • 从 Program.cs 中的 webBuilder.UseStartup<StartupUseStatusCodePages>(); 删除注释。Remove the comments from webBuilder.UseStartup<StartupUseStatusCodePages>(); in Program.cs.
  • 选择主页上的链接。Select the links on the home page on the home page.

备注

状态代码页中间件不捕获异常。The status code pages middleware does not catch exceptions. 若要提供自定义错误处理页,请使用异常处理程序页To provide a custom error handling page, use the exception handler 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:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

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

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

在前面的代码中,{0} 是错误代码的占位符。In the preceding code, {0} is a placeholder for the error code.

具有格式字符串的 UseStatusCodePages 通常不在生产环境中使用,因为它返回对用户没有用的消息。UseStatusCodePages with a format string isn't typically used in production because it returns a message that isn't useful to users.

若要在示例应用中测试 UseStatusCodePages,请从 Program.cs 中的 webBuilder.UseStartup<StartupFormat>(); 删除注释。To test UseStatusCodePages in the sample app, remove the comments from webBuilder.UseStartup<StartupFormat>(); in Program.cs.

包含 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:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

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

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

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

使用 lambda 的 UseStatusCodePages 通常不在生产中使用,因为它返回对用户没有用的消息。UseStatusCodePages with a lambda isn't typically used in production because it returns a message that isn't useful to users.

若要在示例应用中测试 UseStatusCodePages,请从 Program.cs 中的 webBuilder.UseStartup<StartupStatusLambda>(); 删除注释。To test UseStatusCodePages in the sample app, remove the comments from webBuilder.UseStartup<StartupStatusLambda>(); in Program.cs.

UseStatusCodePagesWithRedirectsUseStatusCodePagesWithRedirects

UseStatusCodePagesWithRedirects 扩展方法:The UseStatusCodePagesWithRedirects extension method:

  • 向客户端发送“302 - 已找到”状态代码。Sends a 302 - Found status code to the client.
  • 将客户端重定向到 URL 模板中提供的错误处理终结点。Redirects the client to the error handling endpoint provided in the URL template. 错误处理终结点通常会显示错误信息并返回 HTTP 200。The error handling endpoint typically displays error information and returns HTTP 200.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseStatusCodePagesWithRedirects("/MyStatusCode?code={0}");

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

URL 模板可能会包括状态代码的 {0} 占位符,如前面的代码中所示。The URL template can include a {0} placeholder for the status code, as shown in the preceding code. 如果 URL 模板以波形符 ~(代字号)开头,则 ~ 会替换为应用的 PathBaseIf the URL template starts with ~ (tilde), the ~ is replaced by the app's PathBase. 在应用中指定终结点时,请为终结点创建 MVC 视图或 Razor 页面。When specifying an endpoint in the app, create an MVC view or Razor page for the endpoint. 有关 Razor Pages 示例,请参阅示例应用中的 Pages/MyStatusCode.cshtmlFor a Razor Pages example, see Pages/MyStatusCode.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.

若要在示例应用中测试 UseStatusCodePages,请从 Program.cs 中的 webBuilder.UseStartup<StartupSCredirect>(); 删除注释。To test UseStatusCodePages in the sample app, remove the comments from webBuilder.UseStartup<StartupSCredirect>(); in Program.cs.

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.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseStatusCodePagesWithReExecute("/MyStatusCode2", "?code={0}");

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

如果在应用中指定终结点,请为终结点创建 MVC 视图或 Razor 页面。If an endpoint within the app is specified, create an MVC view or Razor page for the endpoint. 确保将 UseStatusCodePagesWithReExecute 放置在 UseRouting 之前,以便可以将请求重新路由到状态页。Ensure UseStatusCodePagesWithReExecute is placed before UseRouting so the request can be rerouted to the status page. 有关 Razor Pages 示例,请参阅示例应用中的 Pages/MyStatusCode2.cshtmlFor a Razor Pages example, see Pages/MyStatusCode2.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 /.

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

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class MyStatusCode2Model : PageModel
{
    public string RequestId { get; set; }
    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

    public string ErrorStatusCode { get; set; }

    public string OriginalURL { get; set; }
    public bool ShowOriginalURL => !string.IsNullOrEmpty(OriginalURL);

    public void OnGet(string code)
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
        ErrorStatusCode = code;

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

有关 Razor Pages 示例,请参阅示例应用中的 Pages/MyStatusCode2.cshtmlFor a Razor Pages example, see Pages/MyStatusCode2.cshtml in the sample app.

若要在示例应用中测试 UseStatusCodePages,请从 Program.cs 中的 webBuilder.UseStartup<StartupSCreX>(); 删除注释。To test UseStatusCodePages in the sample app, remove the comments from webBuilder.UseStartup<StartupSCreX>(); in Program.cs.

禁用状态代码页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:

public void OnGet()
{
    // using Microsoft.AspNetCore.Diagnostics;
    var statusCodePagesFeature = HttpContext.Features.Get<IStatusCodePagesFeature>();

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

异常处理代码Exception-handling code

异常处理页中的代码可能也会引发异常。Code in exception handling pages can also throw exceptions. 应彻底测试生产错误页面,并格外小心,避免引发其自己的异常。Production error pages should be tested thoroughly and take extra care to avoid throwing exceptions of their own.

响应头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 an app, the HTTP server implementation can handle some exceptions. 如果服务器在发送响应标头之前捕获到异常,服务器将发送不包含响应正文的 500 - Internal Server Error 响应。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 the 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 应用服务和 IIS 上的 ASP.NET Core 进行故障排除For more information, see 对 Azure 应用服务和 IIS 上的 ASP.NET Core 进行故障排除.

数据库错误页Database error page

数据库开发人员页面异常筛选器 AddDatabaseDeveloperPageExceptionFilter 捕获可以使用 Entity Framework Core 迁移解决的与数据库相关的异常。The Database developer page exception filter AddDatabaseDeveloperPageExceptionFilter captures database-related exceptions that can be resolved by using Entity Framework Core migrations. 当这些异常出现时,便会生成 HTML 响应,其中包含用于解决问题的可能操作的详细信息。When these exceptions occur, an HTML response is generated with details of possible actions to resolve the issue. 仅在开发环境中启用此页。This page is enabled only in the Development environment. 在指定个人用户帐户时,ASP.NET Core Razor Pages 模板生成以下代码:The following code was generated by the ASP.NET Core Razor Pages templates when individual user accounts were specified:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDatabaseDeveloperPageExceptionFilter();
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();
}

异常筛选器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 exceptions that occur during the execution of a controller action or another filter. 有关详细信息,请参阅 ASP.NET Core 中的筛选器For more information, see ASP.NET Core 中的筛选器.

异常筛选器适合捕获 MVC 操作内发生的异常,但它们不如内置异常处理中间件 UseExceptionHandler 灵活。Exception filters are useful for trapping exceptions that occur within MVC actions, but they're not as flexible as the built-in exception handling middleware, UseExceptionHandler. 我们建议使用 UseExceptionHandler,除非你需要根据选择的 MVC 操作以不同的方式执行错误处理。We recommend using UseExceptionHandler, unless you need to perform error handling differently based on which MVC action is chosen.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

模型状态错误Model state errors

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

其他资源Additional resources

作者:Tom DykstraSteve SmithBy Tom Dykstra, 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. 下载方法。)(How to download.)

开发人员异常页Developer Exception Page

开发人员异常页显示请求异常的详细信息。The Developer Exception Page displays detailed information about request exceptions. ASP.NET Core 模板会生成以下代码:The ASP.NET Core templates generate the following code:

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

当应用在开发环境中运行时,前面的代码启用开发人员异常页。The preceding code enables the developer exception page when the app is running in the Development environment.

模板将 UseDeveloperExceptionPage 放在任何中间件之前,以便捕获后面的中间件中的异常。The templates place UseDeveloperExceptionPage before any middleware so exceptions are caught in the middleware that follows.

仅当应用程序在开发环境中运行时,前面的代码才启用开发人员异常页。The preceding code enables the Developer Exception Page only when the app is running in the Development environment. 当应用在生产环境中运行时,不应公开显示详细的异常信息。Detailed exception information should not be displayed publicly when the app runs in production. 有关配置环境的详细信息,请参阅 在 ASP.NET Core 中使用多个环境For more information on configuring environments, see 在 ASP.NET Core 中使用多个环境.

开发人员异常页包括关于异常和请求的以下信息:The Developer Exception Page includes the following information about the exception and the request:

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

异常处理程序页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. 模板生成的代码将请求重新执行到 /ErrorThe template generated code re-executes the request to /Error.

在下面的示例中,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 应用模板在 Pages 文件夹中提供了一个“错误”页面 (.cshtml) 和 PageModel 类 (ErrorModel) 。The Razor Pages app template provides an Error page (.cshtml) and PageModel class (ErrorModel) in the Pages folder. 对于 MVC 应用,项目模板包括错误操作方法和主控制器的错误视图。For an MVC app, the project template includes an Error action method and an Error view in the Home controller.

不要使用 HTTP 方法属性(如 HttpGet)标记错误处理程序操作方法。Don't mark 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 if unauthenticated users should see 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:

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class ErrorModel : PageModel
{
    public string RequestId { get; set; }
    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
    public string ExceptionMessage { get; set; }

    public void OnGet()
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;

        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.

若要触发前面的异常处理页,请将环境设置为生产环境并强制引发异常。To trigger the preceding exception handling page, set the environment to productions and force an exception.

异常处理程序 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>();

            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 错误消息。In the preceding code, await context.Response.WriteAsync(new string(' ', 512)); is added so the Internet Explorer browser displays the error message rather than an IE error message. 有关详细信息,请参阅此 GitHub 问题For more information, see this GitHub issue.

警告

不要向客户端提供来自 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.Diagnostics 包提供。The middleware is made available by the Microsoft.AspNetCore.Diagnostics package.

若要启用常见错误状态代码的默认纯文本处理程序,请在 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).

未使用 UseStatusCodePages 时,导航到没有终结点的 URL 会返回一条与浏览器相关的错误消息,指示找不到终结点。When UseStatusCodePages isn't used, navigating to a URL without an endpoint returns a browser dependent error message indicating the endpoint can't be found. 例如,导航到 Home/Privacy2For example, navigating to Home/Privacy2. 调用 UseStatusCodePages 时,浏览器返回:When UseStatusCodePages is called, the browser returns:

Status Code: 404; Not Found

包含格式字符串的 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 ~ (tilde), the ~ 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. 确保将 UseStatusCodePagesWithReExecute 放置在 UseRouting 之前,以便可以将请求重新路由到状态页。Ensure UseStatusCodePagesWithReExecute is placed before UseRouting so the request can be rerouted to the status page. 有关 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 指令接受可选路径段值: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 应用服务和 IIS 上的 ASP.NET Core 进行故障排除For more information, see 对 Azure 应用服务和 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();
}

UseDatabaseErrorPage 需要 Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore NuGet 包。UseDatabaseErrorPage requires the Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore NuGet package.

异常筛选器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