Skip to content

Commit f842fcf

Browse files
committed
Support application subscribing to OnNotFound and setting NotFoundEventArgs.Path.
1 parent 1efded6 commit f842fcf

File tree

3 files changed

+114
-4
lines changed

3 files changed

+114
-4
lines changed

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

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -391,15 +391,53 @@ private void OnLocationChanged(object sender, LocationChangedEventArgs args)
391391

392392
private void OnNotFound(object sender, NotFoundEventArgs args)
393393
{
394-
if (_renderHandle.IsInitialized && NotFoundPage != null)
394+
bool renderContentIsProvided = NotFoundPage != null || args.Path != null;
395+
if (_renderHandle.IsInitialized && renderContentIsProvided)
395396
{
396-
// setting the path signals to the endpoint renderer that router handled rendering
397-
args.Path = _notFoundPageRoute;
398397
Log.DisplayingNotFound(_logger);
398+
if (NotFoundPage == null && !string.IsNullOrEmpty(args.Path))
399+
{
400+
// The path can be set by a subscriber not defined in blazor framework.
401+
_renderHandle.Render(builder => RenderComponentByRoute(builder, args.Path));
402+
return;
403+
}
404+
405+
// Having the path set signals to the endpoint renderer that router handled rendering.
406+
args.Path = _notFoundPageRoute;
399407
RenderNotFound();
400408
}
401409
}
402410

411+
private void RenderComponentByRoute(RenderTreeBuilder builder, string route)
412+
{
413+
var componentType = FindComponentTypeByRoute(route);
414+
415+
if (componentType != null)
416+
{
417+
builder.OpenComponent<RouteView>(0);
418+
builder.AddAttribute(1, nameof(RouteView.RouteData),
419+
new RouteData(componentType, new Dictionary<string, object>()));
420+
builder.CloseComponent();
421+
}
422+
}
423+
424+
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
425+
private Type? FindComponentTypeByRoute(string route)
426+
{
427+
RefreshRouteTable();
428+
var normalizedRoute = route.StartsWith('/') ? route : $"/{route}";
429+
430+
var context = new RouteContext(normalizedRoute);
431+
Routes.Route(context);
432+
433+
if (context.Handler is not null && typeof(IComponent).IsAssignableFrom(context.Handler))
434+
{
435+
return context.Handler;
436+
}
437+
438+
return null;
439+
}
440+
403441
private void RenderNotFound()
404442
{
405443
_renderHandle.Render(builder =>

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ private void AssertBrowserDefaultNotFoundViewRendered()
138138
);
139139
}
140140

141+
private void AssertLandingPageRendered() =>
142+
Browser.Equal("Any content", () => Browser.Exists(By.Id("test-info")).Text);
143+
141144
private void AssertNotFoundPageRendered()
142145
{
143146
Browser.Equal("Welcome On Custom Not Found Page", () => Browser.FindElement(By.Id("test-info")).Text);
@@ -183,6 +186,30 @@ public void NotFoundSetOnInitialization_ResponseNotStarted_SSR(bool hasReExecuti
183186
AssertUrlNotChanged(testUrl);
184187
}
185188

189+
[Theory]
190+
[InlineData(true, true)]
191+
[InlineData(true, false)]
192+
[InlineData(false, true)]
193+
[InlineData(false, false)]
194+
// 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)
196+
{
197+
string streamingPath = streaming ? "-streaming" : "";
198+
string testUrl = $"{ServerPathBase}/set-not-found-ssr{streamingPath}?useCustomRouter={customRouter}&appSetsEventArgsPath=true";
199+
Navigate(testUrl);
200+
201+
bool onlyReExecutionCouldRenderNotFoundPage = !streaming && customRouter;
202+
if (onlyReExecutionCouldRenderNotFoundPage)
203+
{
204+
AssertLandingPageRendered();
205+
}
206+
else
207+
{
208+
AssertNotFoundPageRendered();
209+
}
210+
AssertUrlNotChanged(testUrl);
211+
}
212+
186213
[Theory]
187214
[InlineData(true, true)]
188215
[InlineData(true, false)]

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

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
@using Components.TestServer.RazorComponents.Pages.Forms
1+
@implements IDisposable
2+
@using Components.TestServer.RazorComponents.Pages.Forms
23
@using Components.WasmMinimal.Pages.NotFound
34
@using TestContentPackage.NotFound
45
@using Components.TestServer.RazorComponents
@@ -12,7 +13,39 @@
1213
[SupplyParameterFromQuery(Name = "useCustomRouter")]
1314
public string? UseCustomRouter { get; set; }
1415

16+
[Parameter]
17+
[SupplyParameterFromQuery(Name = "appSetsEventArgsPath")]
18+
public bool AppSetsEventArgsPath { get; set; }
19+
1520
private Type? NotFoundPageType { get; set; }
21+
private NavigationManager _navigationManager = default!;
22+
23+
[Inject]
24+
private NavigationManager NavigationManager
25+
{
26+
get => _navigationManager;
27+
set
28+
{
29+
_navigationManager = value;
30+
}
31+
}
32+
33+
private void OnNotFoundEvent(object sender, NotFoundEventArgs e)
34+
{
35+
var type = typeof(CustomNotFoundPage);
36+
var routeAttributes = type.GetCustomAttributes(typeof(RouteAttribute), inherit: true);
37+
if (routeAttributes.Length == 0)
38+
{
39+
throw new InvalidOperationException($"The type {type.FullName} " +
40+
$"does not have a {typeof(RouteAttribute).FullName} applied to it.");
41+
}
42+
43+
var routeAttribute = (RouteAttribute)routeAttributes[0];
44+
if (routeAttribute.Template != null)
45+
{
46+
e.Path = routeAttribute.Template;
47+
}
48+
}
1649

1750
protected override void OnParametersSet()
1851
{
@@ -24,6 +57,18 @@
2457
{
2558
NotFoundPageType = null;
2659
}
60+
if (AppSetsEventArgsPath && _navigationManager is not null)
61+
{
62+
_navigationManager.OnNotFound += OnNotFoundEvent;
63+
}
64+
}
65+
66+
public void Dispose()
67+
{
68+
if (AppSetsEventArgsPath)
69+
{
70+
_navigationManager.OnNotFound -= OnNotFoundEvent;
71+
}
2772
}
2873
}
2974

0 commit comments

Comments
 (0)