Skip to content

Commit 496d430

Browse files
committed
Allow Router to bypass OnNavigateAsync guard during 404 re-execution.
1 parent f2fb811 commit 496d430

File tree

4 files changed

+35
-7
lines changed

4 files changed

+35
-7
lines changed

src/Components/Components/src/Routing/RouteTable.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ public void Route(RouteContext routeContext)
7878

7979
private static void ProcessParameters(InboundRouteEntry entry, RouteValueDictionary routeValues)
8080
{
81+
routeValues.Remove(Router.AllowRenderDuringPendingNavigationKey);
82+
8183
// Add null values for unused route parameters.
8284
if (entry.UnusedRouteParameterNames != null)
8385
{

src/Components/Components/src/Routing/Router.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ static readonly IReadOnlyDictionary<string, object> _emptyParametersDictionary
4242

4343
private bool _onNavigateCalled;
4444

45+
internal const string AllowRenderDuringPendingNavigationKey = "__BlazorAllowRenderDuringPendingNavigation";
46+
4547
[Inject] private NavigationManager NavigationManager { get; set; }
4648

4749
[Inject] private INavigationInterception NavigationInterception { get; set; }
@@ -220,11 +222,14 @@ private void ClearRouteCaches()
220222

221223
internal virtual void Refresh(bool isNavigationIntercepted)
222224
{
225+
var providerRouteData = RoutingStateProvider?.RouteData;
226+
var allowRenderDuringPendingNavigation = TryConsumeAllowRenderDuringPendingNavigation(providerRouteData);
227+
223228
// If an `OnNavigateAsync` task is currently in progress, then wait
224229
// for it to complete before rendering. Note: because _previousOnNavigateTask
225230
// is initialized to a CompletedTask on initialization, this will still
226231
// allow first-render to complete successfully.
227-
if (_previousOnNavigateTask.Status != TaskStatus.RanToCompletion)
232+
if (_previousOnNavigateTask.Status != TaskStatus.RanToCompletion && !allowRenderDuringPendingNavigation)
228233
{
229234
if (Navigating != null)
230235
{
@@ -239,7 +244,7 @@ internal virtual void Refresh(bool isNavigationIntercepted)
239244
ComponentsActivityHandle activityHandle;
240245

241246
// In order to avoid routing twice we check for RouteData
242-
if (RoutingStateProvider?.RouteData is { } endpointRouteData)
247+
if (providerRouteData is { } endpointRouteData)
243248
{
244249
activityHandle = RecordDiagnostics(endpointRouteData.PageType.FullName, endpointRouteData.Template);
245250

@@ -312,6 +317,17 @@ internal virtual void Refresh(bool isNavigationIntercepted)
312317
_renderHandle.ComponentActivitySource?.StopNavigateActivity(activityHandle, null);
313318
}
314319

320+
private static bool TryConsumeAllowRenderDuringPendingNavigation(RouteData? routeData)
321+
{
322+
if (routeData?.RouteValues.TryGetValue(AllowRenderDuringPendingNavigationKey, out var value) == true && value is true)
323+
{
324+
(routeData.RouteValues as IDictionary<string, object?>)?.Remove(AllowRenderDuringPendingNavigationKey);
325+
return true;
326+
}
327+
328+
return false;
329+
}
330+
315331
private ComponentsActivityHandle RecordDiagnostics(string componentType, string template)
316332
{
317333
ComponentsActivityHandle activityHandle = default;

src/Components/Endpoints/src/RazorComponentEndpointInvoker.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ await _renderer.InitializeStandardComponentServicesAsync(
8787
context,
8888
componentType: pageComponent,
8989
handler: result.HandlerName,
90-
form: result.HandlerName != null && context.Request.HasFormContentType ? await context.Request.ReadFormAsync() : null);
90+
form: result.HandlerName != null && context.Request.HasFormContentType ? await context.Request.ReadFormAsync() : null,
91+
allowRenderingDuringPendingNavigation: isReExecuted);
9192

9293
// Matches MVC's MemoryPoolHttpResponseStreamWriterFactory.DefaultBufferSize
9394
var defaultBufferSize = 16 * 1024;

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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ internal partial class EndpointHtmlRenderer : StaticHtmlRenderer, IComponentPrer
5454

5555
private string _notFoundUrl = string.Empty;
5656

57+
private const string AllowRenderDuringPendingNavigationKey = "__BlazorAllowRenderDuringPendingNavigation";
58+
5759
public EndpointHtmlRenderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory)
5860
: base(serviceProvider, loggerFactory)
5961
{
@@ -81,12 +83,13 @@ internal async Task InitializeStandardComponentServicesAsync(
8183
HttpContext httpContext,
8284
[DynamicallyAccessedMembers(Component)] Type? componentType = null,
8385
string? handler = null,
84-
IFormCollection? form = null)
86+
IFormCollection? form = null,
87+
bool allowRenderingDuringPendingNavigation = false)
8588
{
8689
var navigationManager = httpContext.RequestServices.GetRequiredService<NavigationManager>();
8790
((IHostEnvironmentNavigationManager)navigationManager)?.Initialize(
88-
GetContextBaseUri(httpContext.Request),
89-
GetFullUri(httpContext.Request),
91+
GetContextBaseUri(httpContext.Request),
92+
GetFullUri(httpContext.Request),
9093
uri => GetErrorHandledTask(OnNavigateTo(uri)));
9194

9295
navigationManager?.OnNotFound += (sender, args) => NotFoundEventArgs = args;
@@ -132,7 +135,13 @@ internal async Task InitializeStandardComponentServicesAsync(
132135
{
133136
// Saving RouteData to avoid routing twice in Router component
134137
var routingStateProvider = httpContext.RequestServices.GetRequiredService<EndpointRoutingStateProvider>();
135-
routingStateProvider.RouteData = new RouteData(componentType, httpContext.GetRouteData().Values);
138+
var routeValues = new RouteValueDictionary(httpContext.GetRouteData().Values);
139+
if (allowRenderingDuringPendingNavigation)
140+
{
141+
routeValues[AllowRenderDuringPendingNavigationKey] = true;
142+
}
143+
144+
routingStateProvider.RouteData = new RouteData(componentType, routeValues);
136145
if (httpContext.GetEndpoint() is RouteEndpoint routeEndpoint)
137146
{
138147
routingStateProvider.RouteData.Template = routeEndpoint.RoutePattern.RawText;

0 commit comments

Comments
 (0)