Skip to content

Commit 10ef000

Browse files
authored
Blazor 404 code re-execution is compatible with OnNavigateAsync (#64034)
1 parent 913ff28 commit 10ef000

File tree

6 files changed

+92
-36
lines changed

6 files changed

+92
-36
lines changed

src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.Prerendering.cs

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -168,30 +168,7 @@ private async Task WaitForResultReady(bool waitForQuiescence, PrerenderedCompone
168168
}
169169
else if (_nonStreamingPendingTasks.Count > 0)
170170
{
171-
if (_isReExecuted)
172-
{
173-
HandleNonStreamingTasks();
174-
}
175-
else
176-
{
177-
await WaitForNonStreamingPendingTasks();
178-
}
179-
}
180-
}
181-
182-
public void HandleNonStreamingTasks()
183-
{
184-
if (NonStreamingPendingTasksCompletion == null)
185-
{
186-
foreach (var task in _nonStreamingPendingTasks)
187-
{
188-
_ = GetErrorHandledTask(task);
189-
}
190-
191-
// Clear the pending tasks since we are handling them
192-
_nonStreamingPendingTasks.Clear();
193-
194-
NonStreamingPendingTasksCompletion = Task.CompletedTask;
171+
await WaitForNonStreamingPendingTasks();
195172
}
196173
}
197174

src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ internal async Task InitializeStandardComponentServicesAsync(
8585
{
8686
var navigationManager = httpContext.RequestServices.GetRequiredService<NavigationManager>();
8787
((IHostEnvironmentNavigationManager)navigationManager)?.Initialize(
88-
GetContextBaseUri(httpContext.Request),
89-
GetFullUri(httpContext.Request),
88+
GetContextBaseUri(httpContext.Request),
89+
GetFullUri(httpContext.Request),
9090
uri => GetErrorHandledTask(OnNavigateTo(uri)));
9191

9292
navigationManager?.OnNotFound += (sender, args) => NotFoundEventArgs = args;
@@ -168,11 +168,6 @@ protected override ComponentState CreateComponentState(int componentId, ICompone
168168

169169
protected override void AddPendingTask(ComponentState? componentState, Task task)
170170
{
171-
if (_isReExecuted)
172-
{
173-
return;
174-
}
175-
176171
var streamRendering = componentState is null
177172
? false
178173
: ((EndpointComponentState)componentState).StreamRendering;

src/Components/test/E2ETest/ServerRenderingTests/NoInteractivityTest.cs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Net.Http;
4+
using System;
55
using Components.TestServer.RazorComponents;
66
using Microsoft.AspNetCore.Components.E2ETest;
77
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
@@ -125,6 +125,23 @@ public void BrowserNavigationToNotExistingPath_ReExecutesTo404(bool streaming)
125125
AssertReExecutionPageRendered();
126126
}
127127

128+
[Fact]
129+
public void BrowserNavigationToNotExistingPath_WithOnNavigateAsync_ReExecutesTo404()
130+
{
131+
AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", isEnabled: true);
132+
Navigate($"{ServerPathBase}/reexecution/not-existing-page?useOnNavigateAsync=true");
133+
AssertReExecutionPageRendered();
134+
}
135+
136+
[Fact]
137+
public void BrowserNavigationToNotExistingPath_WithOnNavigateAsync_ReExecutesTo404_CanStream()
138+
{
139+
AppContext.SetSwitch("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.DisableThrowNavigationException", isEnabled: true);
140+
Navigate($"{ServerPathBase}/streaming-reexecution/not-existing-page?useOnNavigateAsync=true");
141+
AssertReExecutionPageRendered();
142+
Browser.Equal("Streaming completed.", () => Browser.Exists(By.Id("reexecute-streaming-status")).Text);
143+
}
144+
128145
private void AssertReExecutionPageRendered() =>
129146
Browser.Equal("Welcome On Page Re-executed After Not Found Event", () => Browser.Exists(By.Id("test-info")).Text);
130147

@@ -192,7 +209,7 @@ public void NotFoundSetOnInitialization_ResponseNotStarted_SSR(bool hasReExecuti
192209
[InlineData(false, true)]
193210
[InlineData(false, false)]
194211
// This tests the application subscribing to OnNotFound event and setting NotFoundEventArgs.Path, opposed to the framework doing it for the app.
195-
public void NotFoundSetOnInitialization_ApplicationSubscribesToNotFoundEventToSetNotFoundPath_SSR (bool streaming, bool customRouter)
212+
public void NotFoundSetOnInitialization_ApplicationSubscribesToNotFoundEventToSetNotFoundPath_SSR(bool streaming, bool customRouter)
196213
{
197214
string streamingPath = streaming ? "-streaming" : "";
198215
string testUrl = $"{ServerPathBase}/set-not-found-ssr{streamingPath}?useCustomRouter={customRouter}&appSetsEventArgsPath=true";

src/Components/test/testassets/Components.TestServer/RazorComponentEndpointsNoInteractivityStartup.cs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using Components.TestServer.RazorComponents;
99
using Components.TestServer.RazorComponents.Pages.Forms;
1010
using Components.TestServer.Services;
11+
using Microsoft.AspNetCore.Diagnostics;
1112
using Microsoft.AspNetCore.Mvc;
1213

1314
namespace TestServer;
@@ -64,7 +65,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
6465
app.UseExceptionHandler("/Error", createScopeForErrors: true);
6566
}
6667

67-
reexecutionApp.UseStatusCodePagesWithReExecute("/not-found-reexecute", createScopeForStatusCodePages: true);
68+
ConfigureReexecutionPipeline(reexecutionApp, "/not-found-reexecute");
6869
reexecutionApp.UseStaticFiles();
6970
reexecutionApp.UseRouting();
7071
RazorComponentEndpointsStartup<TRootComponent>.UseFakeAuthState(reexecutionApp);
@@ -75,11 +76,37 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
7576
.AddAdditionalAssemblies(Assembly.Load("TestContentPackage"));
7677
});
7778
});
79+
app.Map("/streaming-reexecution", reexecutionApp =>
80+
{
81+
ConfigureReexecutionPipeline(reexecutionApp, "/not-found-reexecute-streaming");
82+
reexecutionApp.UseRouting();
83+
reexecutionApp.UseAntiforgery();
84+
reexecutionApp.UseEndpoints(endpoints =>
85+
{
86+
endpoints.MapRazorComponents<TRootComponent>()
87+
.AddAdditionalAssemblies(Assembly.Load("TestContentPackage"));
88+
});
89+
});
7890

7991
ConfigureSubdirPipeline(app, env);
8092
});
8193
}
8294

95+
private void ConfigureReexecutionPipeline(IApplicationBuilder pipeline, string pathFormat)
96+
{
97+
pipeline.UseStatusCodePagesWithReExecute(pathFormat, createScopeForStatusCodePages: true);
98+
pipeline.Use(async (context, next) =>
99+
{
100+
var reexecute = context.Features.Get<IStatusCodeReExecuteFeature>();
101+
if (reexecute is not null && !string.IsNullOrEmpty(reexecute.OriginalQueryString))
102+
{
103+
context.Request.QueryString = new QueryString(reexecute.OriginalQueryString);
104+
}
105+
106+
await next();
107+
});
108+
}
109+
83110
private void ConfigureSubdirPipeline(IApplicationBuilder app, IWebHostEnvironment env)
84111
{
85112
if (!env.IsDevelopment())

src/Components/test/testassets/Components.TestServer/RazorComponents/App.razor

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
@using Components.WasmMinimal.Pages.NotFound
44
@using TestContentPackage.NotFound
55
@using Components.TestServer.RazorComponents
6+
@using Microsoft.AspNetCore.Components
7+
@using Microsoft.AspNetCore.Components.Routing
8+
@using Microsoft.AspNetCore.Components.Web
9+
@using System.Threading.Tasks
610

711
@code {
812
[Parameter]
@@ -17,6 +21,10 @@
1721
[SupplyParameterFromQuery(Name = "appSetsEventArgsPath")]
1822
public bool AppSetsEventArgsPath { get; set; }
1923

24+
[Parameter]
25+
[SupplyParameterFromQuery(Name = "useOnNavigateAsync")]
26+
public bool ShouldDelayOnNavigateAsync { get; set; }
27+
2028
private Type? NotFoundPageType { get; set; }
2129
private NavigationManager _navigationManager = default!;
2230

@@ -70,6 +78,21 @@
7078
_navigationManager.OnNotFound -= OnNotFoundEvent;
7179
}
7280
}
81+
82+
private Task HandleOnNavigateAsync(NavigationContext args)
83+
{
84+
if (!ShouldDelayOnNavigateAsync)
85+
{
86+
return Task.CompletedTask;
87+
}
88+
89+
return PerformOnNavigateAsyncWork();
90+
}
91+
92+
private async Task PerformOnNavigateAsyncWork()
93+
{
94+
await Task.Yield();
95+
}
7396
}
7497

7598
<!DOCTYPE html>
@@ -93,7 +116,7 @@
93116
{
94117
@if (NotFoundPageType is not null)
95118
{
96-
<Router AppAssembly="@typeof(App).Assembly" AdditionalAssemblies="new[] { typeof(TestContentPackage.NotFound.NotFoundPage).Assembly }" NotFoundPage="NotFoundPageType">
119+
<Router AppAssembly="@typeof(App).Assembly" AdditionalAssemblies="new[] { typeof(TestContentPackage.NotFound.NotFoundPage).Assembly }" NotFoundPage="NotFoundPageType" OnNavigateAsync="HandleOnNavigateAsync">
97120
<Found Context="routeData">
98121
<RouteView RouteData="@routeData" />
99122
<FocusOnNavigate RouteData="@routeData" Selector="[data-focus-on-navigate]" />
@@ -102,7 +125,7 @@
102125
}
103126
else
104127
{
105-
<Router AppAssembly="@typeof(App).Assembly" AdditionalAssemblies="new[] { typeof(TestContentPackage.NotFound.NotFoundPage).Assembly }">
128+
<Router AppAssembly="@typeof(App).Assembly" AdditionalAssemblies="new[] { typeof(TestContentPackage.NotFound.NotFoundPage).Assembly }" OnNavigateAsync="HandleOnNavigateAsync">
106129
<Found Context="routeData">
107130
<RouteView RouteData="@routeData" />
108131
<FocusOnNavigate RouteData="@routeData" Selector="[data-focus-on-navigate]" />
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
@page "/not-found-reexecute-streaming"
2+
@attribute [StreamRendering(true)]
3+
4+
<ReExecutedComponent/>
5+
6+
<p id="reexecute-streaming-status">@_status</p>
7+
8+
@code {
9+
private string _status = "Streaming in progress...";
10+
11+
protected override async Task OnInitializedAsync()
12+
{
13+
await Task.Yield();
14+
_status = "Streaming completed.";
15+
await InvokeAsync(StateHasChanged);
16+
}
17+
}

0 commit comments

Comments
 (0)