From 6df90344f541e0af51a9cf59c0d7f378cbab327e Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Thu, 8 Jul 2021 14:30:35 -0500 Subject: [PATCH 1/4] Blazor error boundaries --- .../blazor/fundamentals/handle-errors.md | 8 +- .../handle-errors/error-boundaries.md | 89 +++++++++++++++++++ .../global-exception-handling.md | 2 + aspnetcore/toc.yml | 2 + 4 files changed, 97 insertions(+), 4 deletions(-) create mode 100644 aspnetcore/blazor/includes/handle-errors/error-boundaries.md diff --git a/aspnetcore/blazor/fundamentals/handle-errors.md b/aspnetcore/blazor/fundamentals/handle-errors.md index 3c787da8e107..39c371a6dded 100644 --- a/aspnetcore/blazor/fundamentals/handle-errors.md +++ b/aspnetcore/blazor/fundamentals/handle-errors.md @@ -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 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)†](/azure/azure-monitor/app/app-insights-overview), to record error information that it receives from clients. @@ -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 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). diff --git a/aspnetcore/blazor/includes/handle-errors/error-boundaries.md b/aspnetcore/blazor/includes/handle-errors/error-boundaries.md new file mode 100644 index 000000000000..8f1d38e0e9b6 --- /dev/null +++ b/aspnetcore/blazor/includes/handle-errors/error-boundaries.md @@ -0,0 +1,89 @@ +--- +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. + +To define an error boundary, use the `ErrorBoundary` component to wrap existing content. The `ErrorBoundary` component: + +* Renders its child content when an error hasn't occurred. +* Renders error Razor markup when an unhandled exception is thrown. + +For example, an error boundary can be added around the body content of the app's main layout. + +`Shared/MainLayout.razor`: + +```razor +
+
+ About +
+ +
+ + @Body + +
+
+``` + +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 and error UI is rendered: + +> An error has occurred! + +By default, the `ErrorBoundary` component renders an empty `
` 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, so you're free to customize the error UI styles. + +You can also change the default error content by setting the `ErrorContent` property: + +```razor + + + @Body + + +

Nothing to see here right now. Sorry!

+
+
+``` + +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. However, we recommend narrowly scoping error boundaries. If you do broadly scope an error boundary, you can reset the error boundary to a non-error state on subsequent page navigations by calling the `Recover` method on the error boundary: + +```razor +... + + + @Body + + +... + +@code { + private ErrorBoundary errorBoundary; + + protected override void OnParametersSet() + { + errorBoundary?.Recover(); + } +} +``` + +::: moniker-end diff --git a/aspnetcore/blazor/includes/handle-errors/global-exception-handling.md b/aspnetcore/blazor/includes/handle-errors/global-exception-handling.md index ab0b5f6d07f8..d249c63b9529 100644 --- a/aspnetcore/blazor/includes/handle-errors/global-exception-handling.md +++ b/aspnetcore/blazor/includes/handle-errors/global-exception-handling.md @@ -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. diff --git a/aspnetcore/toc.yml b/aspnetcore/toc.yml index e64f35f9e661..0c85284b5e92 100644 --- a/aspnetcore/toc.yml +++ b/aspnetcore/toc.yml @@ -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 From 62a07a1efb355d5f3f396a35479ba654846c9b0f Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Thu, 8 Jul 2021 14:38:37 -0500 Subject: [PATCH 2/4] Updates --- .../includes/handle-errors/error-boundaries.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/aspnetcore/blazor/includes/handle-errors/error-boundaries.md b/aspnetcore/blazor/includes/handle-errors/error-boundaries.md index 8f1d38e0e9b6..a91e9b8d0898 100644 --- a/aspnetcore/blazor/includes/handle-errors/error-boundaries.md +++ b/aspnetcore/blazor/includes/handle-errors/error-boundaries.md @@ -10,7 +10,7 @@ Error boundaries provide a convenient approach for handling exceptions. To define an error boundary, use the `ErrorBoundary` component to wrap existing content. The `ErrorBoundary` component: * Renders its child content when an error hasn't occurred. -* Renders error Razor markup when an unhandled exception is thrown. +* Renders error UI when an unhandled exception is thrown. For example, an error boundary can be added around the body content of the app's main layout. @@ -30,7 +30,9 @@ For example, an error boundary can be added around the body content of the app's
``` -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. +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`: @@ -46,11 +48,14 @@ private void IncrementCount() } ``` -If the unhandled exception is thrown for a `currentCount` over five, the exception is handled by the error boundary and error UI is rendered: +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 `
` 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, so you're free to customize the error UI styles. +By default, the `ErrorBoundary` component renders an empty `
` 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, so you're free to customize the error UI. You can also change the default error content by setting the `ErrorContent` property: @@ -65,7 +70,7 @@ You can also change the default error content by setting the `ErrorContent` prop ``` -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. However, we recommend narrowly scoping error boundaries. If you do broadly scope an error boundary, you can reset the error boundary to a non-error state on subsequent page navigations by calling the `Recover` method on the error boundary: +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 `Recover` method on the error boundary: ```razor ... From fa5cf662f5e46d2a69502a895c05d4816f79fc8c Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Thu, 8 Jul 2021 14:43:10 -0500 Subject: [PATCH 3/4] Updates --- .../includes/handle-errors/error-boundaries.md | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/aspnetcore/blazor/includes/handle-errors/error-boundaries.md b/aspnetcore/blazor/includes/handle-errors/error-boundaries.md index a91e9b8d0898..0c4efaedbcd7 100644 --- a/aspnetcore/blazor/includes/handle-errors/error-boundaries.md +++ b/aspnetcore/blazor/includes/handle-errors/error-boundaries.md @@ -5,23 +5,17 @@ no-loc: [Home, Privacy, Kestrel, appsettings.json, "ASP.NET Core Identity", cook ## Error boundaries -Error boundaries provide a convenient approach for handling exceptions. - -To define an error boundary, use the `ErrorBoundary` component to wrap existing content. The `ErrorBoundary` component: +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. -For example, an error boundary can be added around the body content of the app's main layout. +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
-
- About -
-
@Body @@ -51,9 +45,7 @@ private void IncrementCount() 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! +* Error UI is rendered (`An error has occurred!`). By default, the `ErrorBoundary` component renders an empty `
` 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, so you're free to customize the error UI. @@ -70,7 +62,7 @@ You can also change the default error content by setting the `ErrorContent` prop ``` -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 `Recover` method on the error boundary: +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 ... From 208a86d6659faea659440a0ce2264d4e3a970014 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Thu, 8 Jul 2021 14:50:19 -0500 Subject: [PATCH 4/4] Updates --- aspnetcore/blazor/includes/handle-errors/error-boundaries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnetcore/blazor/includes/handle-errors/error-boundaries.md b/aspnetcore/blazor/includes/handle-errors/error-boundaries.md index 0c4efaedbcd7..046599c8c2b7 100644 --- a/aspnetcore/blazor/includes/handle-errors/error-boundaries.md +++ b/aspnetcore/blazor/includes/handle-errors/error-boundaries.md @@ -47,7 +47,7 @@ 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 `
` 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, so you're free to customize the error UI. +By default, the `ErrorBoundary` component renders an empty `
` 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: