dotnet / AspNetCore.Docs

Documentation for ASP.NET Core
https://docs.microsoft.com/aspnet/core
Creative Commons Attribution 4.0 International
12.6k stars 25.29k forks source link

ErrorBoundary approach for global exception handling with per-page/component rendering #33109

Closed guardrex closed 3 months ago

guardrex commented 3 months ago

Description

Reported by @TimMurphy ...

It's because of the way I think <ErrorBoundary> works that I started down the rabbit hole of using the alternative global exception handling section.

I've created https://github.com/TimMurphy/ErrorBoundaryWithPerPageInteracvtity. It is BWA with per-page/component mode.

In the generated /counter page I've wrapped the HTML with <ErrorBoundary>. Press the button 3 times and you'll see the <ErrorBoundary> has no effect.

I've added a WorkingCounter page that works with <ErrorBoundary>. This solution involves having a page, ie WorkingCounter, with a <ErrorBoundary> and the actual content for the page in a separate file, ie. WorkingCounterComponent.

Is this the correct way to use <ErrorBoundary>? Seems like more work than it should be.

How I'm using per-page/component mode is the public side of the site SSR and the admin section, /admin, I'm using Interactive rendering. I'd like the entire site to be interactive rendering but from the documentation I've read this is not a good idea because it may require higher spec server and I'm trying to keep costs to a minimum and use the lowest server configuration Azure provides. Am I wrong in this approach?

Page URL

https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/handle-errors?view=aspnetcore-8.0

Content source URL

https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/blazor/fundamentals/handle-errors.md

Document ID

66a0b1c2-c45c-98ca-9808-6f340a861c44

Article author

@guardrex

guardrex commented 3 months ago

I opened a new issue because we don't work off of closed issues/PRs.

The critical concept in this section in a per-page/component scenario is this line (emphasis added) ...

If the error boundary is interactive, it's capable of activating for Interactive Server-rendered components that it wraps.

... and that's a bit buried πŸ™ˆ in there. The reason it isn't clearer is ...

This happens for a simple reason: We're cranking along full speed πŸƒβ€β™‚οΈπŸ˜… to get coverage into place for a given release, and rushing rarely ever results in any good outcomes ... documentation or anything else for that matter. We then circle around later ... like now πŸ˜„ ... and fix up what we placed.

I'm going to fix this section today. In the meantime to see how the ErrorBoundary must wrap a component that throws, here's an example for a per-page/component app ...

Components/EmbeddedCounter.razor:

<h1>Embedded Counter</h1>

<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;

        if (currentCount > 5)
        {
            throw new InvalidOperationException("Current count is over five!");
        }
    }
}

Components/Pages/Home.razor:

@page "/"
@rendermode InteractiveServer

<PageTitle>Home</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new app.

<ErrorBoundary>
    <ChildContent>
        <EmbeddedCounter />
    </ChildContent>
    <ErrorContent>
        <p class="errorUI">😈 A rotten gremlin got us. Sorry!</p>
    </ErrorContent>
</ErrorBoundary>

Run it without debugging, or else the debugger will jump in on the exception and cause you to have to continue to see the result.

I'll probably include that exact example in the section, and I'm going to split it into subsections to deal with global/per-page. I'll also work on the phrasing about what the ErrorBoundary is supposed to wrap, either markup with components that throw or components that throw.