Skip to content

Commit 598a68f

Browse files
committed
Add NotFoundPage.
1 parent c89cd57 commit 598a68f

File tree

5 files changed

+89
-9
lines changed

5 files changed

+89
-9
lines changed

src/Components/Components/src/PublicAPI.Unshipped.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#nullable enable
22
Microsoft.AspNetCore.Components.NavigationManager.NotFoundEvent -> System.EventHandler<System.EventArgs!>!
33
virtual Microsoft.AspNetCore.Components.NavigationManager.NotFound() -> void
4+
Microsoft.AspNetCore.Components.Routing.Router.NotFoundPage.get -> System.Type!
5+
Microsoft.AspNetCore.Components.Routing.Router.NotFoundPage.set -> void
46
Microsoft.AspNetCore.Components.Infrastructure.ComponentStatePersistenceManager.ComponentStatePersistenceManager(Microsoft.Extensions.Logging.ILogger<Microsoft.AspNetCore.Components.Infrastructure.ComponentStatePersistenceManager!>! logger, System.IServiceProvider! serviceProvider) -> void
57
Microsoft.AspNetCore.Components.Infrastructure.ComponentStatePersistenceManager.SetPlatformRenderMode(Microsoft.AspNetCore.Components.IComponentRenderMode! renderMode) -> void
68
Microsoft.AspNetCore.Components.Infrastructure.RegisterPersistentComponentStateServiceCollectionExtensions

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

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#nullable disable warnings
55

6+
using System.Diagnostics.CodeAnalysis;
67
using System.Reflection;
78
using System.Reflection.Metadata;
89
using System.Runtime.ExceptionServices;
@@ -70,6 +71,13 @@ static readonly IReadOnlyDictionary<string, object> _emptyParametersDictionary
7071
[Parameter]
7172
public RenderFragment NotFound { get; set; }
7273

74+
/// <summary>
75+
/// Gets or sets the page content to display when no match is found for the requested route.
76+
/// </summary>
77+
[Parameter]
78+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
79+
public Type NotFoundPage { get; set; } = default!;
80+
7381
/// <summary>
7482
/// Gets or sets the content to display when a match is found for the requested route.
7583
/// </summary>
@@ -132,6 +140,22 @@ public async Task SetParametersAsync(ParameterView parameters)
132140
throw new InvalidOperationException($"The {nameof(Router)} component requires a value for the parameter {nameof(Found)}.");
133141
}
134142

143+
if (NotFoundPage != null)
144+
{
145+
if (!typeof(IComponent).IsAssignableFrom(NotFoundPage))
146+
{
147+
throw new InvalidOperationException($"The type {NotFoundPage.FullName} " +
148+
$"does not implement {typeof(IComponent).FullName}.");
149+
}
150+
151+
var routeAttributes = NotFoundPage.GetCustomAttributes(typeof(RouteAttribute), inherit: true);
152+
if (routeAttributes.Length == 0)
153+
{
154+
throw new InvalidOperationException($"The type {NotFoundPage.FullName} " +
155+
$"does not have a {typeof(RouteAttribute).FullName} applied to it.");
156+
}
157+
}
158+
135159
if (!_onNavigateCalled)
136160
{
137161
_onNavigateCalled = true;
@@ -327,7 +351,22 @@ private void OnNotFound(object sender, EventArgs args)
327351
if (_renderHandle.IsInitialized)
328352
{
329353
Log.DisplayingNotFound(_logger);
330-
_renderHandle.Render(NotFound ?? DefaultNotFoundContent);
354+
_renderHandle.Render(builder =>
355+
{
356+
if (NotFoundPage != null)
357+
{
358+
builder.OpenComponent(0, NotFoundPage);
359+
builder.CloseComponent();
360+
}
361+
else if (NotFound != null)
362+
{
363+
NotFound(builder);
364+
}
365+
else
366+
{
367+
DefaultNotFoundContent(builder);
368+
}
369+
});
331370
}
332371
}
333372

src/Components/test/E2ETest/Tests/GlobalInteractivityTest.cs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,16 @@ public class GlobalInteractivityTest(
2222
{
2323

2424
[Theory]
25-
[InlineData("server", true)]
26-
[InlineData("webassembly", true)]
27-
[InlineData("ssr", false)]
28-
public void CanRenderNotFoundInteractive(string renderingMode, bool isInteractive)
25+
[InlineData("server", true, false)]
26+
[InlineData("webassembly", true, false)]
27+
[InlineData("server", true, true)]
28+
[InlineData("webassembly", true, true)]
29+
[InlineData("ssr", false, true)]
30+
[InlineData("ssr", false, false)]
31+
public void CanRenderNotFoundPage(string renderingMode, bool isInteractive, bool useCustomNotFoundPage)
2932
{
30-
Navigate($"/subdir/render-not-found-{renderingMode}");
33+
string query = useCustomNotFoundPage ? "?useCustomNotFoundPage=true" : "";
34+
Navigate($"{ServerPathBase}/render-not-found-{renderingMode}{query}");
3135

3236
if (isInteractive)
3337
{
@@ -36,8 +40,16 @@ public void CanRenderNotFoundInteractive(string renderingMode, bool isInteractiv
3640
Browser.Exists(By.Id(buttonId)).Click();
3741
}
3842

39-
var bodyText = Browser.FindElement(By.TagName("body")).Text;
40-
Assert.Contains("There's nothing here", bodyText);
43+
if (useCustomNotFoundPage)
44+
{
45+
var infoText = Browser.FindElement(By.Id("test-info")).Text;
46+
Assert.Contains("Welcome On Custom Not Found Page", infoText);
47+
}
48+
else
49+
{
50+
var bodyText = Browser.FindElement(By.TagName("body")).Text;
51+
Assert.Contains("There's nothing here", bodyText);
52+
}
4153
}
4254

4355
[Fact]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@page "/render-custom-not-found-page"
2+
3+
<h3 id="test-info">Welcome On Custom Not Found Page</h3>
4+
<p>Sorry, the page you are looking for does not exist.</p>

src/Components/test/testassets/Components.WasmMinimal/Routes.razor

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,29 @@
11
@using Microsoft.AspNetCore.Components.Routing
22

3-
<Router AppAssembly="@typeof(Program).Assembly">
3+
@using Components.WasmMinimal.Pages
4+
@inject NavigationManager NavigationManager
5+
6+
@code {
7+
private Type? NotFoundPageType { get; set; }
8+
9+
protected override void OnInitialized()
10+
{
11+
var uri = NavigationManager.ToAbsoluteUri(NavigationManager.Uri);
12+
var query = System.Web.HttpUtility.ParseQueryString(uri.Query);
13+
var useCustomNotFoundPage = query["useCustomNotFoundPage"];
14+
15+
if (useCustomNotFoundPage == "true")
16+
{
17+
NotFoundPageType = typeof(CustomNotFoundPage);
18+
}
19+
else
20+
{
21+
NotFoundPageType = null;
22+
}
23+
}
24+
}
25+
26+
<Router AppAssembly="@typeof(Program).Assembly" NotFoundPage="NotFoundPageType">
427
<Found Context="routeData">
528
<RouteView RouteData="@routeData" />
629
<FocusOnNavigate RouteData="@routeData" Selector="h1" />

0 commit comments

Comments
 (0)