Skip to content

Latest commit

 

History

History
325 lines (223 loc) · 16.6 KB

error-handling.md

File metadata and controls

325 lines (223 loc) · 16.6 KB
title author description monikerRange ms.author ms.custom ms.date uid
Handle errors in ASP.NET Core
tdykstra
Discover how to handle errors in ASP.NET Core apps.
>= aspnetcore-2.1
tdykstra
mvc
03/02/2019
fundamentals/error-handling

Handle errors in ASP.NET Core

By Tom Dykstra, Luke Latham, and Steve Smith

This article covers common approaches to handling errors in ASP.NET Core apps.

View or download sample code (how to download)

Developer Exception Page

To configure an app to display a page that shows detailed information about exceptions, use the Developer Exception Page. The page is made available by the Microsoft.AspNetCore.Diagnostics package, which is available in the Microsoft.AspNetCore.App metapackage. Add a line to the Startup.Configure method when the app is running in the Development environment:

[!code-csharp]

Place the call to xref:Microsoft.AspNetCore.Builder.DeveloperExceptionPageExtensions.UseDeveloperExceptionPage* in front of any middleware where you want to catch exceptions.

Warning

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. For more information on configuring environments, see xref:fundamentals/environments.

To see the Developer Exception Page, run the sample app with the environment set to Development and add ?throw=true to the base URL of the app. The page includes the following information about the exception and the request:

  • Stack trace
  • Query string parameters (if any)
  • Cookies (if any)
  • Headers

Configure a custom exception handling page

When the app isn't running in the Development environment, call the xref:Microsoft.AspNetCore.Builder.ExceptionHandlerExtensions.UseExceptionHandler* extension method to add Exception Handling Middleware. The middleware:

  • Catches exceptions.
  • 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.

In the following example from the sample app, xref:Microsoft.AspNetCore.Builder.ExceptionHandlerExtensions.UseExceptionHandler* adds the Exception Handling Middleware in non-Development environments. The extension method specifies an error page or controller at the /Error endpoint for re-executed requests after exceptions are caught and logged in Startup.Configure:

[!code-csharp]

The Razor Pages app template provides an Error page (.cshtml) and xref:Microsoft.AspNetCore.Mvc.RazorPages.PageModel class (ErrorModel) in the Pages folder.

In an MVC app, the following error handler method is included in the MVC app template and appears in the Home controller:

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

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

If you need to access the exception in a controller or page, obtain xref:Microsoft.AspNetCore.Diagnostics.IExceptionHandlerFeature from HttpContext.Features and read the xref:System.Exception?displayProperty=fullName (IExceptionHandlerFeature.Error):

// using Microsoft.AspNetCore.Diagnostics;

var exceptionHandlerFeature = 
    HttpContext.Features.Get<IExceptionHandlerFeature>();
var error = exceptionHandlerFeature?.Error;

Warning

Do not serve sensitive error information from xref:Microsoft.AspNetCore.Diagnostics.IExceptionHandlerFeature to clients. Serving sensitive information from errors can compromise the app's security.

Configure custom exception handling code

An alternative to serving an endpoint for errors with a custom exception handling page is to provide a lambda to xref:Microsoft.AspNetCore.Builder.ExceptionHandlerExtensions.UseExceptionHandler*. This approach permits the app to work directly with the error before returning a response.

The sample app demonstrates the approach in Startup.Configure. Set the preprocessor directive at the top of the Startup.cs file to LambdaErrorHandler and run the app in the Production environment. After the app starts, trigger an exception with the Throw Exception link on the Index page. The following lambda runs:

[!code-csharp]

Warning

Do not serve sensitive error information from xref:Microsoft.AspNetCore.Diagnostics.IExceptionHandlerFeature to clients. Serving sensitive information from errors can compromise the app's security. The sample app serves sensitive information from IExceptionHandlerFeature.Error Exception.Message for demonstration purposes only!

For more information on using preprocessor directives with documentation sample apps, see xref:index#preprocessor-directives-in-sample-code.

Configure status code pages

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.

The middleware is made available by the Microsoft.AspNetCore.Diagnostics package, which is available in the Microsoft.AspNetCore.App metapackage.

Add a line to the Startup.Configure method:

app.UseStatusCodePages();

Call the xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePages* method before request handling middleware (for example, Static File Middleware and MVC Middleware).

By default, Status Code Pages Middleware adds text-only handlers for common status codes, such as 404 - Not Found:

Status Code: 404; Not Found

The middleware supports several extension methods that allow you to customize its behavior.

An overload of xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePages* takes a lambda expression, which you can use to process custom error-handling logic and manually write the response:

[!code-csharp]

An overload of xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePages* takes a content type and format string, which you can use to customize the content type and response text:

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

Redirect and re-execute extension methods

xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithRedirects*:

  • Sends a 302 - Found status code to the client.
  • Redirects the client to the location provided in the URL template.

[!code-csharp]

xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithRedirects* is commonly used when the app:

  • Should redirect the client to a different endpoint, usually in cases where a different app processes the error. 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.

xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithReExecute*:

  • Returns the original status code to the client.
  • Generates the response body by re-executing the request pipeline using an alternate path.
app.UseStatusCodePagesWithReExecute("/Error/{0}");

xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithReExecute* is commonly used when the app should:

  • Process the request without redirecting to a different endpoint. For web apps, the client's browser address bar reflects the originally requested endpoint.
  • Preserve and return the original status code with the response.

Templates may include a placeholder ({0}) for the status code. The template must start with a forward slash (/). When using a placeholder, confirm that the endpoint (page or controller) can process the path segment. For example, a Razor Page for errors should accept the optional path segment value with the @page directive:

@page "{code?}"

Status code pages can be disabled for specific requests in a Razor Pages handler method or in an MVC controller. To disable status code pages, attempt to retrieve the xref:Microsoft.AspNetCore.Diagnostics.IStatusCodePagesFeature from the request's HttpContext.Features collection and disable the feature if it's available:

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

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

To use a xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePages* overload that points to an endpoint within the app, create an MVC view or Razor Page for the endpoint. For example, the Razor Pages app template produces the following page and page model class:

Error.cshtml:

::: moniker range=">= aspnetcore-2.2"

@page
@model ErrorModel
@{
    ViewData["Title"] = "Error";
}

<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>

@if (Model.ShowRequestId)
{
    <p>
        <strong>Request ID:</strong> <code>@Model.RequestId</code>
    </p>
}

<h3>Development Mode</h3>
<p>
    Swapping to the <strong>Development</strong> environment displays 
    detailed information about the error that occurred.
</p>
<p>
    <strong>The Development environment shouldn't be enabled for deployed 
    applications.</strong> It can result in displaying sensitive information 
    from exceptions to end users. For local debugging, enable the 
    <strong>Development</strong> environment by setting the 
    <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to 
    <strong>Development</strong> and restarting the app.
</p>

Error.cshtml.cs:

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

    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

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

::: moniker-end

::: moniker range="< aspnetcore-2.2"

@page
@model ErrorModel
@{
    ViewData["Title"] = "Error";
}

<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>

@if (Model.ShowRequestId)
{
    <p>
        <strong>Request ID:</strong> <code>@Model.RequestId</code>
    </p>
}

<h3>Development Mode</h3>
<p>
    Swapping to <strong>Development</strong> environment will display more detailed 
    information about the error that occurred.
</p>
<p>
    <strong>Development environment should not be enabled in deployed applications
    </strong>, as it can result in sensitive information from exceptions being 
    displayed to end users. For local debugging, development environment can be 
    enabled by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment 
    variable to <strong>Development</strong>, and restarting the application.
</p>

Error.cshtml.cs:

public class ErrorModel : PageModel
{
    public string RequestId { get; set; }

    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

    [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, 
        NoStore = true)]
    public void OnGet()
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
    }
}

::: moniker-end

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.

Also, be aware that 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

In addition to the exception handling logic in your app, the server implementation can handle some exceptions. 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 is handled by the server's exception handling. Any configured custom error pages or exception handling middleware or filters don't affect this behavior.

Startup exception handling

Only the hosting layer can handle exceptions that take place during app startup. Using Web Host, you can configure how the host behaves in response to errors during startup with the captureStartupErrors and detailedErrors keys.

Hosting can only show an error page for a captured startup error if the error occurs after host address/port binding. If any binding fails for any reason:

  • The hosting layer logs a critical exception.
  • The dotnet process crashes.
  • No error page is displayed when the app is running on the Kestrel server.

When running on IIS or IIS Express, a 502.5 - Process Failure is returned by the ASP.NET Core Module if the process can't start. For more information, see xref:host-and-deploy/iis/troubleshoot. For information on troubleshooting startup issues with Azure App Service, see xref:host-and-deploy/azure-apps/troubleshoot.

ASP.NET Core MVC error handling

MVC apps have some additional options for handling errors, such as configuring exception filters and performing model validation.

Exception filters

Exception filters can be configured globally or on a per-controller or per-action basis in an MVC app. These filters handle any unhandled exception that occurs during the execution of a controller action or another filter. These filters aren't called otherwise. For more information, see xref:mvc/controllers/filters#exception-filters.

Tip

Exception filters are useful for trapping exceptions that occur within MVC actions, but they're not as flexible as error handling middleware. We recommend the use of middleware. Use filters only where you need to perform error handling differently based on which MVC action is chosen.

Handle model state errors

Model validation occurs prior to invoking each controller action, and it's the action method's responsibility to inspect ModelState.IsValid and react appropriately.

Some apps choose to follow a standard convention for dealing with model validation errors, in which case a filter may be an appropriate place to implement such a policy. You should test how your actions behave with invalid model states. For more information, see xref:mvc/controllers/testing.

Additional resources

  • xref:host-and-deploy/azure-iis-errors-reference
  • xref:host-and-deploy/iis/troubleshoot
  • xref:host-and-deploy/azure-apps/troubleshoot