Skip to content

Commit

Permalink
Handling errors update
Browse files Browse the repository at this point in the history
  • Loading branch information
guardrex committed Mar 2, 2019
1 parent ba5a272 commit 09c3420
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 14 deletions.
40 changes: 34 additions & 6 deletions aspnetcore/fundamentals/error-handling.md
Expand Up @@ -5,7 +5,7 @@ description: Discover how to handle errors in ASP.NET Core apps.
monikerRange: '>= aspnetcore-2.1'
ms.author: tdykstra
ms.custom: mvc
ms.date: 03/01/2019
ms.date: 03/02/2019
uid: fundamentals/error-handling
---
# Handle errors in ASP.NET Core
Expand All @@ -18,9 +18,9 @@ This article covers common approaches to handling errors in ASP.NET Core apps.

## 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](https://www.nuget.org/packages/Microsoft.AspNetCore.Diagnostics/) package, which is available in the [Microsoft.AspNetCore.App metapackage](xref:fundamentals/metapackage-app). Add a line to the `Startup.Configure` method:
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](https://www.nuget.org/packages/Microsoft.AspNetCore.Diagnostics/) package, which is available in the [Microsoft.AspNetCore.App metapackage](xref:fundamentals/metapackage-app). Add a line to the `Startup.Configure` method when the app is running in the Development [environment](xref:fundamentals/environments):

[!code-csharp[](error-handling/samples/2.x/ErrorHandlingSample/Startup.cs?name=snippet_DevExceptionPage&highlight=5)]
[!code-csharp[](error-handling/samples/2.x/ErrorHandlingSample/Startup.cs?name=snippet_UseDeveloperExceptionPage)]

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

Expand All @@ -42,9 +42,9 @@ When the app isn't running in the Development environment, call the <xref:Micros
* 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 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[](error-handling/samples/2.x/ErrorHandlingSample/Startup.cs?name=snippet_DevExceptionPage&highlight=9)]
[!code-csharp[](error-handling/samples/2.x/ErrorHandlingSample/Startup.cs?name=snippet_UseExceptionHandler1)]

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

Expand All @@ -61,6 +61,34 @@ public IActionResult Error()

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](xref:Microsoft.AspNetCore.Http.HttpContext.Features) and read the <xref:System.Exception?displayProperty=fullName> ([IExceptionHandlerFeature.Error](xref:Microsoft.AspNetCore.Diagnostics.IExceptionHandlerFeature.Error)):

```csharp
// 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](#configure-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](xref:fundamentals/environments). After the app starts, trigger an exception with the **Throw Exception** link on the Index page. The following lambda runs:

[!code-csharp[](error-handling/samples/2.x/ErrorHandlingSample/Startup.cs?name=snippet_UseExceptionHandler2)]

> [!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](xref:Microsoft.AspNetCore.Diagnostics.IExceptionHandlerFeature.Error) [Exception.Message](xref:System.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.
Expand Down Expand Up @@ -279,7 +307,7 @@ When running on [IIS](/iis) or [IIS Express](/iis/extensions/introduction-to-iis

### 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. To learn more, see <xref:mvc/controllers/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.
Expand Down
@@ -1,10 +1,12 @@
#define StatusCodePages // or StatusCodePagesWithRedirect
#define StatusCodePages // or StatusCodePagesWithRedirect
#define PageErrorHandler // or LambdaErrorHandler

using System;
using System.Net;
using System.Text;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
Expand All @@ -17,18 +19,55 @@ public void ConfigureServices(IServiceCollection services)
{
}

#region snippet_DevExceptionPage
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
#region snippet_UseDeveloperExceptionPage
app.UseDeveloperExceptionPage();
#endregion
}
else
{
#if ErrorPageHandler
#region snippet_UseExceptionHandler1
app.UseExceptionHandler("/Error");
#endregion
#endif
#if LambdaErrorHandler
#region snippet_UseExceptionHandler2
// using Microsoft.AspNetCore.Diagnostics;

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 error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null)
{
// FOR DEMONSTRATION PURPOSES ONLY!
// Do NOT expose an error message to the client
// with the following code.
await context.Response.WriteAsync("Error: " +
HtmlEncoder.Default.Encode(error.Error.Message) +
"<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
});
});
#endregion
#endif
}
#endregion

#if StatusCodePages
#region snippet_StatusCodePages
Expand Down Expand Up @@ -58,23 +97,31 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
error.Run(async context =>
{
var builder = new StringBuilder();
builder.AppendLine("<html><body>");
builder.AppendLine("An error occurred.<br />");
builder.AppendLine("<html lang=\"en\"><body>");
builder.AppendLine("An error occurred.<br>");
var path = context.Request.Path.ToString();
if (path.Length > 1)
{
builder.AppendLine("Status Code: " +
HtmlEncoder.Default.Encode(path.Substring(1)) + "<br />");
HtmlEncoder.Default.Encode(path.Substring(1)) + "<br>");
}
var referrer = context.Request.Headers["referer"];
if (!string.IsNullOrEmpty(referrer))
{
builder.AppendLine("Return to <a href=\"" +
HtmlEncoder.Default.Encode(referrer) + "\">" +
WebUtility.HtmlEncode(referrer) + "</a><br />");
WebUtility.HtmlEncode(referrer) + "</a><br>");
}
builder.AppendLine("</body></html>");
context.Response.ContentType = "text/html";
await context.Response.WriteAsync(builder.ToString());
});
});
Expand All @@ -85,7 +132,9 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
throw new Exception("Exception triggered!");
}
var builder = new StringBuilder();
builder.AppendLine("<html><body>Hello World!");
builder.AppendLine("<ul>");
builder.AppendLine("<li><a href=\"/?throw=true\">Throw Exception</a></li>");
Expand All @@ -94,6 +143,7 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
builder.AppendLine("</body></html>");
context.Response.ContentType = "text/html";
await context.Response.WriteAsync(builder.ToString());
});
}
Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/fundamentals/middleware/index.md
Expand Up @@ -231,7 +231,7 @@ ASP.NET Core ships with the following middleware components. The *Order* column
| [Authentication](xref:security/authentication/identity) | Provides authentication support. | Before `HttpContext.User` is needed. Terminal for OAuth callbacks. |
| [Cookie Policy](xref:security/gdpr) | Tracks consent from users for storing personal information and enforces minimum standards for cookie fields, such as `secure` and `SameSite`. | Before middleware that issues cookies. Examples: Authentication, Session, MVC (TempData). |
| [CORS](xref:security/cors) | Configures Cross-Origin Resource Sharing. | Before components that use CORS. |
| [Diagnostics](xref:fundamentals/error-handling) | Configures diagnostics. | Before components that generate errors. |
| [Exception Handling](xref:fundamentals/error-handling) | Handles exceptions. | Before components that generate errors. |
| [Forwarded Headers](/dotnet/api/microsoft.aspnetcore.builder.forwardedheadersextensions) | Forwards proxied headers onto the current request. | Before components that consume the updated fields. Examples: scheme, host, client IP, method. |
| [Health Check](xref:host-and-deploy/health-checks) | Checks the health of an ASP.NET Core app and its dependencies, such as checking database availability. | Terminal if a request matches a health check endpoint. |
| [HTTP Method Override](/dotnet/api/microsoft.aspnetcore.builder.httpmethodoverrideextensions) | Allows an incoming POST request to override the method. | Before components that consume the updated method. |
Expand Down

0 comments on commit 09c3420

Please sign in to comment.