Skip to content

Blazor error boundaries #22688

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions aspnetcore/blazor/fundamentals/handle-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ In production, don't render framework exception messages or stack traces in the
* Disclose sensitive information to end users.
* Help a malicious user discover weaknesses in an app that can compromise the security of the app, server, or network.

## Global exception handling

[!INCLUDE[](~/blazor/includes/handle-errors/global-exception-handling.md)]

[!INCLUDE[](~/blazor/includes/handle-errors/error-boundaries.md)]

## Log errors with a persistent provider

If an unhandled exception occurs, the exception is logged to <xref:Microsoft.Extensions.Logging.ILogger> instances configured in the service container. By default, Blazor apps log to console output with the Console Logging Provider. Consider logging to a more permanent location on the server by sending error information to a backend web API that uses a logging provider with log size management and log rotation. Alternatively, the backend web API app can use an Application Performance Management (APM) service, such as [Azure Application Insights (Azure Monitor)&dagger;](/azure/azure-monitor/app/app-insights-overview), to record error information that it receives from clients.
Expand Down Expand Up @@ -328,12 +328,12 @@ In production, don't render framework exception messages or stack traces in the
* Disclose sensitive information to end users.
* Help a malicious user discover weaknesses in an app that can compromise the security of the app, server, or network.

## Global exception handling

[!INCLUDE[](~/blazor/includes/handle-errors/global-exception-handling.md)]

Because the approaches in this section handle errors with a [`try-catch`](/dotnet/csharp/language-reference/keywords/try-catch) statement, the SignalR connection between the client and server isn't broken when an error occurs and the circuit remains alive. Any unhandled exception is fatal to a circuit. For more information, see the preceding section on [how a Blazor Server app reacts to unhandled exceptions](#how-a-blazor-server-app-reacts-to-unhandled-exceptions).

[!INCLUDE[](~/blazor/includes/handle-errors/error-boundaries.md)]

## Log errors with a persistent provider

If an unhandled exception occurs, the exception is logged to <xref:Microsoft.Extensions.Logging.ILogger> instances configured in the service container. By default, Blazor apps log to console output with the Console Logging Provider. Consider logging to a more permanent location on the server with a provider that manages log size and log rotation. Alternatively, the app can use an Application Performance Management (APM) service, such as [Azure Application Insights (Azure Monitor)](/azure/azure-monitor/app/app-insights-overview).
Expand Down
86 changes: 86 additions & 0 deletions aspnetcore/blazor/includes/handle-errors/error-boundaries.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
no-loc: [Home, Privacy, Kestrel, appsettings.json, "ASP.NET Core Identity", cookie, Cookie, Blazor, "Blazor Server", "Blazor WebAssembly", "Identity", "Let's Encrypt", Razor, SignalR]
---
::: moniker range=">= aspnetcore-6.0"

## Error boundaries

Error boundaries provide a convenient approach for handling exceptions. The `ErrorBoundary` component:

* Renders its child content when an error hasn't occurred.
* Renders error UI when an unhandled exception is thrown.

To define an error boundary, use the `ErrorBoundary` component to wrap existing content. For example, an error boundary can be added around the body content of the app's main layout.

`Shared/MainLayout.razor`:

```razor
<div class="main">
<div class="content px-4">
<ErrorBoundary>
@Body
</ErrorBoundary>
</div>
</div>
```

The app continues to function normally, but the error boundary handles unhandled exceptions.

Consider the following example, where the `Counter` component throws an exception if the count increments past five.

In `Pages/Counter.razor`:

```csharp
private void IncrementCount()
{
currentCount++;

if (currentCount > 5)
{
throw new InvalidOperationException("Current count is too big!");
}
}
```

If the unhandled exception is thrown for a `currentCount` over five:

* The exception is handled by the error boundary.
* Error UI is rendered (`An error has occurred!`).

By default, the `ErrorBoundary` component renders an empty `<div>` element with the `blazor-error-boundary` CSS class for its error content. The colors, text, and icon for the default UI are defined using CSS in the app's stylesheet in the `wwwroot` folder, so you're free to customize the error UI.

You can also change the default error content by setting the `ErrorContent` property:

```razor
<ErrorBoundary>
<ChildContent>
@Body
</ChildContent>
<ErrorContent>
<p class="errorUI">Nothing to see here right now. Sorry!</p>
</ErrorContent>
</ErrorBoundary>
```

Because the error boundary is defined in the layout in the preceding examples, the error UI is seen regardless of which page the user navigated to. We recommend narrowly scoping error boundaries in most scenarios. If you do broadly scope an error boundary, you can reset it to a non-error state on subsequent page navigation events by calling the error boundary's `Recover` method:

```razor
...

<ErrorBoundary @ref="errorBoundary">
@Body
</ErrorBoundary>

...

@code {
private ErrorBoundary errorBoundary;

protected override void OnParametersSet()
{
errorBoundary?.Recover();
}
}
```

::: moniker-end
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
---
no-loc: [Home, Privacy, Kestrel, appsettings.json, "ASP.NET Core Identity", cookie, Cookie, Blazor, "Blazor Server", "Blazor WebAssembly", "Identity", "Let's Encrypt", Razor, SignalR]
---
## Global exception handling

Blazor is a single-page application (SPA) client-side framework. The browser serves as the app's host and thus acts as the processing pipeline for individual Razor components based on URI requests for navigation and static assets. Unlike ASP.NET Core apps that run on the server with a middleware processing pipeline, there is no middleware pipeline that processes requests for Razor components that can be leveraged for global error handling. However, an app can use an error processing component as a cascading value to process errors in a centralized way.

The following `Error` component passes itself as a [`CascadingValue`](xref:blazor/components/cascading-values-and-parameters#cascadingvalue-component) to child components. The following example merely logs the error, but methods of the component can process errors in any way required by the app, including through the use of multiple error processing methods. An advantage of using a component over using an [injected service](xref:blazor/fundamentals/dependency-injection) or a custom logger implementation is that a cascaded component can render content and apply CSS styles when an error occurs.
Expand Down
2 changes: 2 additions & 0 deletions aspnetcore/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,8 @@
href: blazor/security/index.md#authorizeview-component
- name: CascadingValue
href: blazor/components/cascading-values-and-parameters.md#cascadingvalue-component
- name: ErrorBoundary
href: blazor/fundamentals/handle-errors.md#error-boundaries
- name: InputCheckbox
href: blazor/forms-validation.md#built-in-form-components
- name: InputDate
Expand Down