From 0317ecf3043662a40679aded94b4da658ee9d5e3 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Tue, 14 Oct 2025 20:12:36 +0200 Subject: [PATCH 1/5] Fix --- .../Components/src/Routing/Router.cs | 16 +++++++---- .../NoInteractivityTest.cs | 20 +++++++++++++ .../RazorComponents/App.razor | 28 +++++++++++++++++-- 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/src/Components/Components/src/Routing/Router.cs b/src/Components/Components/src/Routing/Router.cs index ecb69fe2cf63..70c4ba3efaff 100644 --- a/src/Components/Components/src/Routing/Router.cs +++ b/src/Components/Components/src/Routing/Router.cs @@ -220,17 +220,23 @@ private void ClearRouteCaches() internal virtual void Refresh(bool isNavigationIntercepted) { + var endpointRouteData = RoutingStateProvider?.RouteData; + var navigationInProgress = _previousOnNavigateTask.Status != TaskStatus.RanToCompletion; + // If an `OnNavigateAsync` task is currently in progress, then wait // for it to complete before rendering. Note: because _previousOnNavigateTask // is initialized to a CompletedTask on initialization, this will still // allow first-render to complete successfully. - if (_previousOnNavigateTask.Status != TaskStatus.RanToCompletion) + if (navigationInProgress) { - if (Navigating != null) + if (endpointRouteData is null) { - _renderHandle.Render(Navigating); + if (Navigating != null) + { + _renderHandle.Render(Navigating); + } + return; } - return; } var relativePath = NavigationManager.ToBaseRelativePath(_locationAbsolute.AsSpan()); @@ -239,7 +245,7 @@ internal virtual void Refresh(bool isNavigationIntercepted) ComponentsActivityHandle activityHandle; // In order to avoid routing twice we check for RouteData - if (RoutingStateProvider?.RouteData is { } endpointRouteData) + if (endpointRouteData is not null) { activityHandle = RecordDiagnostics(endpointRouteData.PageType.FullName, endpointRouteData.Template); diff --git a/src/Components/test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs b/src/Components/test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs index a6421eb94689..d7c64f0c559c 100644 --- a/src/Components/test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs +++ b/src/Components/test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Net.Http; using Components.TestServer.RazorComponents; using Microsoft.AspNetCore.Components.E2ETest; @@ -125,6 +126,25 @@ public void BrowserNavigationToNotExistingPath_ReExecutesTo404(bool streaming) AssertReExecutionPageRendered(); } + [Fact] + public void BrowserNavigationToNotExistingPath_WithOnNavigateAsync_ReExecutesTo404() + { + AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", isEnabled: true); + + // using query for controlling router parameters does not work in re-execution scenario, we have to rely on other communication channel + const string useOnNavigateAsyncSwitch = "Components.TestServer.RazorComponents.UseOnNavigateAsync"; + AppContext.SetSwitch(useOnNavigateAsyncSwitch, true); + try + { + Navigate($"{ServerPathBase}/reexecution/not-existing-page"); + AssertReExecutionPageRendered(); + } + finally + { + AppContext.SetSwitch(useOnNavigateAsyncSwitch, false); + } + } + private void AssertReExecutionPageRendered() => Browser.Equal("Welcome On Page Re-executed After Not Found Event", () => Browser.Exists(By.Id("test-info")).Text); diff --git a/src/Components/test/testassets/Components.TestServer/RazorComponents/App.razor b/src/Components/test/testassets/Components.TestServer/RazorComponents/App.razor index f547144c8fdd..76a023f8feff 100644 --- a/src/Components/test/testassets/Components.TestServer/RazorComponents/App.razor +++ b/src/Components/test/testassets/Components.TestServer/RazorComponents/App.razor @@ -3,6 +3,10 @@ @using Components.WasmMinimal.Pages.NotFound @using TestContentPackage.NotFound @using Components.TestServer.RazorComponents +@using Microsoft.AspNetCore.Components +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using System.Threading.Tasks @code { [Parameter] @@ -17,8 +21,13 @@ [SupplyParameterFromQuery(Name = "appSetsEventArgsPath")] public bool AppSetsEventArgsPath { get; set; } + [Parameter] + [SupplyParameterFromQuery(Name = "useOnNavigateAsync")] + public string? UseOnNavigateAsync { get; set; } + private Type? NotFoundPageType { get; set; } private NavigationManager _navigationManager = default!; + private bool ShouldDelayOnNavigateAsync => string.Equals(UseOnNavigateAsync, "true", StringComparison.OrdinalIgnoreCase); [Inject] private NavigationManager NavigationManager @@ -70,6 +79,21 @@ _navigationManager.OnNotFound -= OnNotFoundEvent; } } + + private Task HandleOnNavigateAsync(NavigationContext args) + { + if (!ShouldDelayOnNavigateAsync) + { + return Task.CompletedTask; + } + + return PerformOnNavigateAsyncWork(); + } + + private async Task PerformOnNavigateAsyncWork() + { + await Task.Yield(); + } } @@ -93,7 +117,7 @@ { @if (NotFoundPageType is not null) { - + @@ -102,7 +126,7 @@ } else { - + From c90449bb1631a776a8af275ef43808956bcb1cda Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Date: Wed, 15 Oct 2025 12:10:37 +0200 Subject: [PATCH 2/5] Update src/Components/test/testassets/Components.TestServer/RazorComponents/App.razor Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../testassets/Components.TestServer/RazorComponents/App.razor | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Components/test/testassets/Components.TestServer/RazorComponents/App.razor b/src/Components/test/testassets/Components.TestServer/RazorComponents/App.razor index 76a023f8feff..1c6c272bf8ea 100644 --- a/src/Components/test/testassets/Components.TestServer/RazorComponents/App.razor +++ b/src/Components/test/testassets/Components.TestServer/RazorComponents/App.razor @@ -27,7 +27,8 @@ private Type? NotFoundPageType { get; set; } private NavigationManager _navigationManager = default!; - private bool ShouldDelayOnNavigateAsync => string.Equals(UseOnNavigateAsync, "true", StringComparison.OrdinalIgnoreCase); + private bool ShouldDelayOnNavigateAsync + => bool.TryParse(UseOnNavigateAsync, out var result) && result; [Inject] private NavigationManager NavigationManager From 64df78f408bfb40eb229c10681398240b004a390 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Date: Wed, 15 Oct 2025 12:10:48 +0200 Subject: [PATCH 3/5] Update src/Components/test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs b/src/Components/test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs index d7c64f0c559c..aefef8da43ca 100644 --- a/src/Components/test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs +++ b/src/Components/test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs @@ -1,8 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Net.Http; +using System; using Components.TestServer.RazorComponents; using Microsoft.AspNetCore.Components.E2ETest; using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; From 5ed75df90a5be397846bfeb316de631363f79c09 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Wed, 15 Oct 2025 16:26:56 +0200 Subject: [PATCH 4/5] Cleanup. --- src/Components/Components/src/Routing/Router.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Components/Components/src/Routing/Router.cs b/src/Components/Components/src/Routing/Router.cs index 70c4ba3efaff..7d8e98dff339 100644 --- a/src/Components/Components/src/Routing/Router.cs +++ b/src/Components/Components/src/Routing/Router.cs @@ -221,13 +221,12 @@ private void ClearRouteCaches() internal virtual void Refresh(bool isNavigationIntercepted) { var endpointRouteData = RoutingStateProvider?.RouteData; - var navigationInProgress = _previousOnNavigateTask.Status != TaskStatus.RanToCompletion; // If an `OnNavigateAsync` task is currently in progress, then wait // for it to complete before rendering. Note: because _previousOnNavigateTask // is initialized to a CompletedTask on initialization, this will still // allow first-render to complete successfully. - if (navigationInProgress) + if (_previousOnNavigateTask.Status != TaskStatus.RanToCompletion) { if (endpointRouteData is null) { From b8856ad29dac75d8ab049b6727f58db15a2c6adb Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Wed, 15 Oct 2025 16:29:44 +0200 Subject: [PATCH 5/5] More cleanup. --- src/Components/Components/src/Routing/Router.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Components/Components/src/Routing/Router.cs b/src/Components/Components/src/Routing/Router.cs index 7d8e98dff339..2afa52e99a40 100644 --- a/src/Components/Components/src/Routing/Router.cs +++ b/src/Components/Components/src/Routing/Router.cs @@ -220,20 +220,18 @@ private void ClearRouteCaches() internal virtual void Refresh(bool isNavigationIntercepted) { + // endpointRouterData is populated only in navigations that passed through SSR, including re-executions var endpointRouteData = RoutingStateProvider?.RouteData; // If an `OnNavigateAsync` task is currently in progress, then wait // for it to complete before rendering. Note: because _previousOnNavigateTask // is initialized to a CompletedTask on initialization, this will still // allow first-render to complete successfully. - if (_previousOnNavigateTask.Status != TaskStatus.RanToCompletion) + if (_previousOnNavigateTask.Status != TaskStatus.RanToCompletion && endpointRouteData is null) { - if (endpointRouteData is null) + if (Navigating != null) { - if (Navigating != null) - { - _renderHandle.Render(Navigating); - } + _renderHandle.Render(Navigating); return; } }