Skip to content

Support Razor Component endpoints for UseExceptionHandler #50287

Closed
@SteveSandersonMS

Description

@SteveSandersonMS

Currently it doesn't work to do this:

app.UseExceptionHandler("/Error");

... where /Error corresponds to a Razor Component like:

@page "/Error"
<h1>This is the error page</h1>

In practice what happens is that:

  • Assume some other Razor Component endpoint (SSR) throws an unhandled exception during rendering
  • ExceptionHandlerMiddlewareImpl catches the original unhandled exception, changes the endpoint to /Error (a Razor Component endpoint), and re-runs the pipeline
  • We get back into EndpointHtmlRenderer.InitializeStandardComponentServicesAsync which tries to initialize the same scoped services for a second time.
  • They don't support being initialized for a second time. The first thing that happens to throw is NavigationManager.Initialize (as reported at HttpNavigationManager already initialized on Blazor SSR. #49456)
  • The system falls back on showing the standard error page

Difficulties

This isn't straightforward to resolve for two main reasons:

  1. It really isn't valid to reinitialize or even reuse the same scoped services when re-running the pipeline. The concept of re-running the pipeline without creating a new DI scope is extremely dubious since by definition, the old DI scope is now in an undefined error state - for security, Blazor Server is extremely careful not to let things keep running once they get into an undefined state.
    • Possible solution: We could change RazorComponentEndpointInvoker to guarantee we create a new DI scope for the error endpoint. This would be inconsistent with MVC etc but at least safer. And would solve the double-initialization exceptions.
  2. Even if we can make the endpoint render, it's going to behave badly if you have an interactive router in your layout. Consider:
    • Some Razor Component endpoint at /MyComponentThatThrows throws an unhandled exception
    • The error middleware runs Error.razor, which uses the same Layout.razor, which contains a @rendermode=Server router and <script src="blazor.web.js"></script>
    • The error page starts up a new circuit and runs the router on the server.
    • It will still see the current URL /MyComponentThatThrows and hence now re-runs the same component that throws a second time
    • Possible solution: We somehow would have to emit info into the error page markup to tell the JS-side code not to start any interactive runtime. We might get away with just putting that into the default project template but it would be nasty and error prone if people try to create their own error page from scratch.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-blazorIncludes: Blazor, Razor ComponentsbugThis issue describes a behavior which is not expected - a bug.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions