-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Blazor 404 code re-execution is compatible with OnNavigateAsync
#64034
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
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes an issue where Blazor 404 re-execution was not working properly when the Router component had an OnNavigateAsync handler. The fix allows rendering to proceed during 404 re-execution scenarios while still preventing it during client-side navigation when lazy loading is in progress.
Key changes:
- Modified the Router component to differentiate between server-side rendering (SSR) scenarios and client-side navigation
- Updated the navigation blocking logic to check if route data is available from SSR before blocking rendering
- Added comprehensive test coverage for the 404 re-execution scenario with
OnNavigateAsync
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/Components/Components/src/Routing/Router.cs | Updated navigation logic to allow rendering during SSR/re-execution while maintaining blocking for client navigation |
| src/Components/test/testassets/Components.TestServer/RazorComponents/App.razor | Added test infrastructure with configurable OnNavigateAsync handler and query parameter support |
| src/Components/test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs | Added end-to-end test to verify 404 re-execution works with OnNavigateAsync |
src/Components/test/testassets/Components.TestServer/RazorComponents/App.razor
Outdated
Show resolved
Hide resolved
src/Components/test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs
Outdated
Show resolved
Hide resolved
…onents/App.razor Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…tyTest.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
Failures of |
…NavigateAsync_ReExecutesTo404 test.
src/Components/test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs
Outdated
Show resolved
Hide resolved
...ponents/test/testassets/Components.TestServer/RazorComponents/NavigationCompletionTracker.cs
Show resolved
Hide resolved
oroztocil
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good 👍
|
@javiercn's review: use the information if we are interactive or not instead of putting a flag into route data. |
… when on navigate task has not finished.
| protected virtual void AddPendingTask(ComponentState? componentState, Task task) | ||
| { | ||
| // The pendingTasks collection is only used during prerendering to track quiescence, | ||
| // so will be null at other times. | ||
| AddPendingTask(task); | ||
| } | ||
|
|
||
| internal void AddPendingTask(Task task) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is adding public API.
We can achieve the same effect in a slightly different way without having to add an additional overload nor additional API to RenderHandle.
The issue is that the router doesn't properly notify that it is still doing work, so we reach quiescence without the router being done (and hence we miss on later updates).
Quiescence is tracked whenever we call SetParametersAsync. Instead of doing things this way, we can keep whatever task we need to pass in a field, and on the call to SetParametersAsync we do something like
if (!_renderHandle.RendererInfo.IsInteractive && _taskWeAddedCallingAddPendingTask != null)
{
await _taskWeAddedCallingAddPendingTask;
}That will have the effect of having that task included in the quiescence computation (as SetParametersAsync won't complete until that task completes).
Do not prevent rendering for router that has
OnNavigateAsyncwhen navigation already startedDescription
In #24225 we blocked rendering when
Routerhas a pendingOnNavigateAsynctask to prevent exceptions during lazy loading (#24211). The reason was that rendering was throwing when triggered before with lazy loading finished: #24211. However, this blocking prevented the prerendering quiescence detection mechanism from working correctly, as theOnNavigateAsynctask was not being tracked.This PR fixes the issue by informing the renderer that the
OnNavigateAsynctask is part of the quiescence state, allowing prerendering to wait for it to complete before finalizing the response.Changes
RenderHandle.AddPendingTaskto allow components to register tasks that should be tracked for quiescenceRouter.RunOnNavigateAsyncto register theOnNavigateAsynctask with the render handle using_renderHandle.AddPendingTaskwhenAnyTaskandfirstCompletedTask)BrowserNavigationToNotExistingPath_ReExecutesTo404.OnNavigateAsynccompletion during lazy loading:BrowserNavigationToLazyLoadedRoute_WaitsForOnNavigateAsyncGuard.How it works
During prerendering, the renderer tracks pending tasks to determine when quiescence has been reached. Then, it flushes the http response and the rendering is finished. By registering the
OnNavigateAsynctask with the renderer, we ensure that the end of rendering won't happen beforeOnNavigateAsyncis finished.Fixes #63933