Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 46 additions & 7 deletions src/Components/Components/src/Routing/Router.cs
Original file line number Diff line number Diff line change
Expand Up @@ -391,15 +391,54 @@ private void OnLocationChanged(object sender, LocationChangedEventArgs args)

private void OnNotFound(object sender, NotFoundEventArgs args)
{
if (_renderHandle.IsInitialized && NotFoundPage != null)
bool renderContentIsProvided = NotFoundPage != null || args.Path != null;
if (_renderHandle.IsInitialized && renderContentIsProvided)
{
// setting the path signals to the endpoint renderer that router handled rendering
args.Path = _notFoundPageRoute;
Log.DisplayingNotFound(_logger);
RenderNotFound();
if (!string.IsNullOrEmpty(args.Path))
{
// The path can be set by a subscriber not defined in blazor framework.
_renderHandle.Render(builder => RenderComponentByRoute(builder, args.Path));
}
else
{
// Having the path set signals to the endpoint renderer that router handled rendering.
args.Path = _notFoundPageRoute;
RenderNotFound();
}
Log.DisplayingNotFound(_logger, args.Path);
}
}

private void RenderComponentByRoute(RenderTreeBuilder builder, string route)
{
var componentType = FindComponentTypeByRoute(route);

if (componentType != null)
{
builder.OpenComponent<RouteView>(0);
builder.AddAttribute(1, nameof(RouteView.RouteData),
new RouteData(componentType, new Dictionary<string, object>()));
builder.CloseComponent();
}
}

[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
private Type? FindComponentTypeByRoute(string route)
{
RefreshRouteTable();
var normalizedRoute = route.StartsWith('/') ? route : $"/{route}";

var context = new RouteContext(normalizedRoute);
Routes.Route(context);

if (context.Handler is not null && typeof(IComponent).IsAssignableFrom(context.Handler))
{
return context.Handler;
}

return null;
}

private void RenderNotFound()
{
_renderHandle.Render(builder =>
Expand Down Expand Up @@ -451,8 +490,8 @@ private static partial class Log
[LoggerMessage(3, LogLevel.Debug, "Navigating to non-component URI '{ExternalUri}' in response to path '{Path}' with base URI '{BaseUri}'", EventName = "NavigatingToExternalUri")]
internal static partial void NavigatingToExternalUri(ILogger logger, string externalUri, string path, string baseUri);

[LoggerMessage(4, LogLevel.Debug, $"Displaying {nameof(NotFound)} on request", EventName = "DisplayingNotFoundOnRequest")]
internal static partial void DisplayingNotFound(ILogger logger);
[LoggerMessage(4, LogLevel.Debug, $"Displaying contents of {{displayedContentPath}} on request", EventName = "DisplayingNotFoundOnRequest")]
internal static partial void DisplayingNotFound(ILogger logger, string displayedContentPath);
#pragma warning restore CS0618 // Type or member is obsolete
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ private void AssertBrowserDefaultNotFoundViewRendered()
);
}

private void AssertLandingPageRendered() =>
Browser.Equal("Any content", () => Browser.Exists(By.Id("test-info")).Text);

private void AssertNotFoundPageRendered()
{
Browser.Equal("Welcome On Custom Not Found Page", () => Browser.FindElement(By.Id("test-info")).Text);
Expand Down Expand Up @@ -183,6 +186,30 @@ public void NotFoundSetOnInitialization_ResponseNotStarted_SSR(bool hasReExecuti
AssertUrlNotChanged(testUrl);
}

[Theory]
[InlineData(true, true)]
[InlineData(true, false)]
[InlineData(false, true)]
[InlineData(false, false)]
// This tests the application subscribing to OnNotFound event and setting NotFoundEventArgs.Path, opposed to the framework doing it for the app.
public void NotFoundSetOnInitialization_ApplicationSubscribesToNotFoundEventToSetNotFoundPath_SSR (bool streaming, bool customRouter)
{
string streamingPath = streaming ? "-streaming" : "";
string testUrl = $"{ServerPathBase}/set-not-found-ssr{streamingPath}?useCustomRouter={customRouter}&appSetsEventArgsPath=true";
Navigate(testUrl);

bool onlyReExecutionCouldRenderNotFoundPage = !streaming && customRouter;
if (onlyReExecutionCouldRenderNotFoundPage)
{
AssertLandingPageRendered();
}
else
{
AssertNotFoundPageRendered();
}
AssertUrlNotChanged(testUrl);
}

[Theory]
[InlineData(true, true)]
[InlineData(true, false)]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@using Components.TestServer.RazorComponents.Pages.Forms
@implements IDisposable
@using Components.TestServer.RazorComponents.Pages.Forms
@using Components.WasmMinimal.Pages.NotFound
@using TestContentPackage.NotFound
@using Components.TestServer.RazorComponents
Expand All @@ -12,7 +13,39 @@
[SupplyParameterFromQuery(Name = "useCustomRouter")]
public string? UseCustomRouter { get; set; }

[Parameter]
[SupplyParameterFromQuery(Name = "appSetsEventArgsPath")]
public bool AppSetsEventArgsPath { get; set; }

private Type? NotFoundPageType { get; set; }
private NavigationManager _navigationManager = default!;

[Inject]
private NavigationManager NavigationManager
{
get => _navigationManager;
set
{
_navigationManager = value;
}
}

private void OnNotFoundEvent(object sender, NotFoundEventArgs e)
{
var type = typeof(CustomNotFoundPage);
var routeAttributes = type.GetCustomAttributes(typeof(RouteAttribute), inherit: true);
if (routeAttributes.Length == 0)
{
throw new InvalidOperationException($"The type {type.FullName} " +
$"does not have a {typeof(RouteAttribute).FullName} applied to it.");
}

var routeAttribute = (RouteAttribute)routeAttributes[0];
if (routeAttribute.Template != null)
{
e.Path = routeAttribute.Template;
}
}

protected override void OnParametersSet()
{
Expand All @@ -24,6 +57,18 @@
{
NotFoundPageType = null;
}
if (AppSetsEventArgsPath && _navigationManager is not null)
{
_navigationManager.OnNotFound += OnNotFoundEvent;
}
}

public void Dispose()
{
if (AppSetsEventArgsPath)
{
_navigationManager.OnNotFound -= OnNotFoundEvent;
}
}
}

Expand Down
Loading