Handling errors in ASP.NET Core

This article introduces some ways to deal with common errors in ASP.NET Core Web applications. Related Web API, see Handling ASP.NET Core Web APIerrors.

Developer exception page

The developer exception page displays the detailed information of the request exception. The ASP.NET Core template generates the following code:

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

When the app 开发环境is running in, the previously highlighted code enables the developer exception page.

The template is placed in the front part of the middleware pipeline UseDeveloperExceptionPageso that it can catch exceptions raised in the middleware later.

The preceding code enables the developer exception page only when the application is running in the development environment. When the application is running in a production environment, detailed exception information should not be displayed publicly. For details on configuring environments, see Using multiple environments in ASP.NET Core.

The developer exception page includes the following information about exceptions and requests:

  • Stack trace
  • Query string parameters (if any)
  • Cookie (if any)
  • Header

Exception handler page

To configure a custom error handling page for the production environment, use exception handling middleware. Middleware:

  • Catch and log the exception.
  • Re-execute the request in the alternate pipeline using the indicated path. If the response has been initiated, the request will not be re-executed. Template generated code /Errorpath re-execution request.

In the following example, UseExceptionHandleradd exception handling middleware in a non-development environment:

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

The Razor Pages application template provides an "error" page (.cshtml) and PageModel class (ErrorModel) in the Pages folder. For MVC applications, the project template includes the error operation method and the error view of the main controller.

Do not use HTTP method attributes (such as HttpGet) to mark error handler operation methods. Explicit predicates can prevent certain request access methods. If unauthenticated users should see the error view, allow anonymous access to the method.

The exception is handled in different ways according to the original HTTP method:

  • For Razor Pages, create multiple handler methods. For example, using the OnGetprocess GET exceptions, OnPosthandling POST exception.
  • For MVC, apply HTTP verb attributes to multiple operations. For example, using the [HttpGet]process GET exceptions, [HttpPost]handling POST exception.

To allow unauthenticated users to view the custom error handling page, make sure it supports anonymous access.

Access exception

Use IExceptionHandlerPathFeature to access the exception and original request path in the 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";
        }
    }
}

[!WARNING]
Do not provide sensitive error information to the client. Errors in providing services are a security risk.

To test for exceptions in the sample app:

  • Set the environment to the production environment.
  • Program.cs from the webBuilder.UseStartup<Startup>();comments removed.
  • Select "Trigger Exception" on the homepage.

To trigger the previous exception handling page, please set the environment to the production environment and force an exception to be thrown.

Exception handler lambda

An alternative to a custom exception handler page is to provide a lambda to UseExceptionHandler. Using lambda, you can access the error before returning the response.
The following example shows how to use 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();
}

In the preceding code, await context.Response.WriteAsync(new string('', 512)); is added so that the Internet Explorer browser displays the corresponding error message instead of displaying the IE error message. For more information, see this GitHub issue.
caveat

Do not offer to the client from IExceptionHandlerFeatureor IExceptionHandlerPathFeaturesensitive error messages. Errors in providing services are a security risk.

To view the results of the exception handling lambda in the sample application, please use the ProdEnvironment and ErrorHandlerLambda preprocessor directives, and select "Trigger Exception" on the homepage.

UseStatusCodePages

By default, ASP.NET Core apps will not provide a status code page for HTTP status codes (such as "404-Not Found"). The application returns a status code and an empty response body. To provide a status code page, use status code page middleware.
This middleware is provided through the Microsoft.AspNetCore.Diagnostics package.
To enable the default plain text handler for common error status codes, call UseStatusCodePages in the Startup.Configure method:

app.UseStatusCodePages();

Called before request processing middleware UseStatusCodePages. For example, it is called before static file middleware and endpoint middleware UseStatusCodePages.

Not used UseStatusCodePageswhen there is no endpoint to navigate to the URL will return an error message associated with the browser, indicating not find the endpoint. For example, navigate to Home/Privacy2. Call UseStatusCodePages, the browser returns:

Status Code: 404; Not Found

UseStatusCodePages It is usually not used in production because it returns messages that are not useful to the user.

The status code page middleware does not catch exceptions. To provide a custom error handling page, please use 异常处理程序页.

Contains the format string UseStatusCodePages

To customize the response content type and text, use the UseStatusCodePages overload that requires the content type and format string:

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

In the preceding code, it {0}is a placeholder for the error code.

Containing lambda UseStatusCodePages

To specify custom error handling and response writing codes, use the UseStatusCodePages overload that requires lambda expressions:

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

UseStatusCodePagesWithRedirects Extension method:

  • Send a "302-Found" status code to the client.
  • Redirect the client to the error handling endpoint provided in the URL template. The error handling endpoint usually displays an error message and returns HTTP 200.
app.UseStatusCodePagesWithRedirects("/StatusCode?code={0}");

URL template may include a state code {0}placeholders, as shown in the previous code. If the URL template with a tilde ~(tilde) at the beginning, it ~will be replaced with the application PathBase. When specifying the endpoint in the app, create an MVC view or Razor page for the endpoint. For Razor Pages samples, see Pages/StatusCode.cshtml in the sample application.

This method is usually used when the application:

  • The client should be redirected to a different endpoint (usually when different applications handle errors). For web applications, the client's browser address bar reflects the redirection endpoint.
  • The original status code should not be retained and returned through the initial redirect response.

UseStatusCodePagesWithReExecute

UseStatusCodePagesWithReExecute Extension method:

  • Return the original status code to the client.
  • Re-execute the request pipeline by using the alternate path to generate the response body.
app.UseStatusCodePagesWithReExecute("/StatusCode","?code={0}");

If you point to an endpoint in the app, create an MVC view or Razor page for the endpoint. Ensure UseStatusCodePagesWithReExecuteplaced UseRoutingbefore, so that the request can be re-routed to the status page. For Razor Pages samples, see Pages/StatusCode.cshtml in the sample application.

This method is usually used when the application should:

  • Process the request, but do not redirect to a different endpoint. For web applications, the client's browser address bar reflects the endpoint of the original request.
  • Keep the original status code and return it with the response.

URL templates and query string templates may include placeholders for status codes {0}. URL template must /begin with.

@page "{code?}"

The error handling endpoint 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 page

To disable MVC controller or method of operation status code page, use the [SkipStatusCodePages]feature.

To disable the Razor Pages handler method or the specific request status code page in the MVC controller, use IStatusCodePagesFeature:

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

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

Exception handling code

The code in the exception handling page may also raise an exception. You should thoroughly test the production error page and take extra care to avoid throwing its own exceptions.

Response header

After the response header is sent:

  • The app cannot change the status code of the response.
  • No exception page or handler can be run. The response must be completed or the connection must be aborted.

Server exception handling

In addition to the exception handling logic in the application, it HTTP 服务器实现can also handle some exceptions. If the server before sending the captured response header to abnormal, the server sends a response that does not contain the body of 500 - Internal Server Errorthe response. If the server catches an exception after sending the response header, the server will close the connection. Requests that cannot be processed by the application will be processed by the server. When the server processes the request, any exceptions that occur will be handled by the server's exception handling. The applied custom error pages, exception handling middleware, and filters will not affect this behavior.

Start exception handling

Exceptions that occur during application startup can only be handled at the bearer layer. The host can be configured as, 捕获启动错误and 捕获详细错误.

Only when the error occurs after the host address/port binding, the hosting layer can display the error page of the captured startup error. If the binding fails:

  • The hosting layer will log critical exceptions.
  • The dotnet process crashed.
  • In the HTTP server will not Kestreldisplay any error page.

When running the application on IIS (or Azure Application Service) or IIS Express, if the process cannot be started, the ASP.NET Core module will return "502.5-Process failed". For more information, see Troubleshooting ASP.NET Core on Azure App Service and IIS.

Database error page

The database error page middleware captures database-related exceptions, and Entity Framework migration can be used to resolve these exceptions. When these exceptions occur, an HTML response is generated that contains detailed information about possible actions to solve the problem. This page should only be enabled in a development environment. Enable this page by adding code to Startup.Configure:

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

UseDatabaseErrorPageRequires Microsoft.AspNetCore.Diagnostics.EntityFrameworkCoreNuGet package.

Exception filter

In MVC applications, exception filters can be configured globally or individually for each controller or each operation. In the Razor Pages application, exception filters can be configured globally or individually for each page model. These filters handle any unhandled exceptions that occur while performing controller actions or other filters. For more information, see ASP.NET Core 中的筛选器.

Exception filters are suitable for catching exceptions that occur within MVC operations, but they are not as 异常处理中间件 UseExceptionHandlerflexible as built-in . We recommend using UseExceptionHandlerit unless you need to perform error handling in different ways based on the selected MVC operation.

Model status error

To learn how to handle model status errors, see 模型绑定and 模型验证.


Error handling

Add error handling page

In Startup.cssetting middleware:

app.UseStatusCodePagesWithReExecute("/error/{0}");

Recommended UseStatusCodePagesWithReExecuteinstead of UseStatusCodePagesWithRedirectsthe former execution in the pipeline error jump url, which will be redirected to the url, leading to http error status code becomes normal execution of a new page 200 yards.

Then write the error controller:

public class ErrorController : Controller
{
    [Route("Error/{statusCode}")]
    public IActionResult Index(int statusCode)
    {
        var statusCodeResult = HttpContext.Features.
            Get<IStatusCodeReExecuteFeature>();
        var viewModel = new ErrorViewModel
        {
            Path = statusCodeResult.OriginalPath,
            QueryString = statusCodeResult.
                OriginalQueryString,
        };
        switch (statusCode)
        {
            case 404:
                viewModel.Message = "页面未找到";
                break;
        }
        return View("Error", viewModel);
    }
}

By the way, I also defined ViewModel:

public class ErrorViewModel
{
    public int Code { get; set; }
    public string Message { get; set; }
    public string Path { get; set; }
    public string QueryString { get; set; }
}

The view code will not be posted, it is nothing more than displaying these error messages in the ViewModel~

Set global exception jump

Add middleware

app.UseExceptionHandler("/exception");

To write the processing controller, you need to add AllowAnonymousannotations here to allow users to access this exception page when they are not logged in to ensure that the exception page can be displayed anyway.

[AllowAnonymous]
[Route("exception")]
public IActionResult ExceptionHandler()
{
    var exception = HttpContext.Features.
        Get<IExceptionHandlerPathFeature>();
    var viewModel = new ExceptionViewModel
    {
        Path = exception.Path,
        Message = exception.Error.Message,
        StackTrace = exception.Error.StackTrace,
    };
    return View("Exception", viewModel);
}

In addition, ViewModel is defined as follows:

public class ExceptionViewModel
{
    public string Path { get; set; }
    public string Message { get; set; }
    public string StackTrace { get; set; }
}

Guess you like

Origin blog.csdn.net/WuLex/article/details/113622749