(30/30) Learn Blazor Together: .NET 6 <ErrorBoundary>

(30/30) Learn Blazor Together: .NET 6 <ErrorBoundary>

Yesterday we talked about unit testing, but sometimes due to time constraints, comprehensive testing is not possible.

Last updated 12/25/2021 9:50 PM
StrayaWorker
6 min read
Category
Blazor
Topic
Learn Blazor Together Series
Tags
.NET C# ASP.NET Core Blazor

Yesterday we talked about unit testing, but sometimes due to time constraints, it may not be possible to fully test, which could cause "a component error" to crash "the entire system" (as shown below). This is because Blazor Server establishes a circuit on the server; once an unhandled error occurs, the server terminates the circuit to prevent security issues.

We don't want to "terminate the entire system on every error", nor do we want to "wrap every method in try…catch…" to handle all errors programmatically. That's why we use the new .NET 6 component <ErrorBoundary>.

In the React front-end framework, there is also an ErrorBoundary concept. It catches errors generated by any component and displays a default UI page. When an error occurs, it confines the error to that component, while other components remain functional.

Blazor borrows this concept, but the Blazor team has stated that this cannot catch all possible exceptions. Furthermore, <ErrorBoundary> is not meant to handle global error interception — that's the job of ILogger. The main purpose of <ErrorBoundary> is to handle errors raised during rendering or lifecycle methods (OnInitializedAsync, OnParametersSetAsync). There are also many potential risks, such as developers thinking <ErrorBoundary> can handle all errors, developers feeling they no longer need to create custom UIs for different errors (leading to confusion when multiple different errors occur), or when many <ErrorBoundary> instances of the same type fire simultaneously, and the developer happens to log every error, potentially causing log congestion. Therefore, it cannot be considered the last line of defense for error handling.

Although the above sounds a bit alarming, <ErrorBoundary> is still fine for handling simple page logic. Let's give it a try!

(Note: If you created your project using Visual Studio 2022, you can skip the steps in the next paragraph.)

Since the author uses Visual Studio 2019, and .NET 6 can only be run with Visual Studio 2022, the first step is to download Visual Studio 2022. Then open the BlaozrPractice solution, and change the <TargetFramework> to net6.0 for both the BlazorServer and BlazorServerMsTest projects.

Then remember to add the following class to wwwroot/css/site.css. <ErrorBoundary> generates a <div> with the blazor-error-boundary class.

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

Then, in MainLayout.razor, wrap @Body with <ErrorBoundary>. Finally, go to BlogBase.razor.cs and add a piece of code to throw an error in LoadData().

private async Task LoadData()
{
    …
    throw new Exception("This is a test error message");
}

Open the website and see that this time, instead of seeing the yellow exception error message at the bottom, the default UI is displayed.

However, this message appears for any error, which seems unfriendly to users. Let's customize it using <ChildContent> and <ErrorContent>. These are essentially RenderFragment and can contain any HTML tags or Components.

<ErrorBoundary>
  <ChildContent> @Body </ChildContent>
  <ErrorContent>
    <p>Sorry, an unknown error has occurred. Please contact the administrator.</p>
  </ErrorContent>
</ErrorBoundary>

But if you switch to the Roles or Users pages, you'll notice that the red error message is still on the page. This is because once <ErrorBoundary> detects an error, it displays it. At this point, you need to call the Recover() method. This method resets the error count to 0 and calls StateHasChanged() to notify all components that the state has changed, thus re-rendering the component. Remember to use @ref to reference the specific <ErrorBoundary>, otherwise Recover() will not execute.

…
<div class="content px-4">
  <ErrorBoundary @ref="errorBoundary">
    <ChildContent> @Body </ChildContent>
    <ErrorContent>
      <p>Sorry, an unknown error has occurred. Please contact the administrator.</p>
    </ErrorContent>
  </ErrorBoundary>
</div>
…
@code {
  private ErrorBoundary errorBoundary;
  protected override void OnParametersSet()
  {
    errorBoundary?.Recover();
  }
}

Additionally, <ErrorBoundary> has a property MaximumErrorCount with a default value of 100. If MaximumErrorCount exceeds the specified number, the system will crash.

Reflection

When the author signed up for the IT Ironman Competition, the intention was simply to document the experience of writing the project. At that time, I was worried about not being able to finish the competition. But on the 17th day, I really forgot — it's truly shameful to make such a rookie mistake. In the following days, because I thought I had already failed, I ended up padding the content a bit. That mindset is really not acceptable. The author will merge several articles and post them under a new topic.

Although I couldn't finish the competition, I still learned something from documenting my experiences. Over the past year, even when I wrote down notes from work, they were fragmented records without a full beginning-to-end story. For this competition, to write in detail, I checked many sources multiple times. That's the right way to take notes. I hope to make more progress in next year's competition.

References:

  1. Unhandled Exceptions in Blazor Server with Error Boundaries
  2. Blazor "Error boundaries" design proposal #30940
  3. Blazor .NET 6 - Error Boundaries - Custom UI for Errors
  4. How To Get .NET 6 in Visual Studio 2019

(Note: The author followed the instructions in this video but still could not switch to .NET 6 in Visual Studio 2019. If anyone knows another method, please let me know.)

Ref:

  1. Download .NET SDKs for Visual Studio

(Note: The official Microsoft download site states that .NET 6 does not support the Visual Studio 2019 SDK. It is unclear how the author of the above video managed to do it.)

Keep Exploring

Related Reading

More Articles
Same category / Same tag 12/25/2021

(29/30)Learn Blazor Together: Blazor Unit Testing

The most boring part of developing a system is probably fixing bugs, especially errors like trying to access a null object (`Object reference not set to an instance of an object.`), which is the most common problem most people encounter when they first step into programming. To break free from the tedious bug-fixing process, this article introduces `unit testing`.

Continue Reading
Same category / Same tag 12/25/2021

(28/30) Learning Blazor Together: Policy-based Authorization

It was mentioned earlier that `ASP.NET Core Identity` uses `Claim`-based authentication. In fact, `ASP.NET Core Identity` has different types of authorization methods, the simplest being `Login Authorization`, `Role Authorization`, and `Claim Authorization`. However, all of the above are implemented in one way: Policy-based Authorization.

Continue Reading