Skip to content

Commit ba871ba

Browse files
surayya-MSjaviercnhalter73
authored andcommitted
Unified app model routing (#47759)
* Render app component instead of special host component * implemented RoutingStateProvider * RouteData RouteValues nullable --------- Co-authored-by: jacalvar <[email protected]> Co-authored-by: Stephen Halter <[email protected]>
1 parent 6c08acc commit ba871ba

37 files changed

+349
-150
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ Microsoft.AspNetCore.Components.RenderHandle.DispatchExceptionAsync(System.Excep
1616
*REMOVED*Microsoft.AspNetCore.Components.NavigationManager.ToAbsoluteUri(string! relativeUri) -> System.Uri!
1717
Microsoft.AspNetCore.Components.NavigationManager.ToAbsoluteUri(string? relativeUri) -> System.Uri!
1818
Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.SetEventHandlerName(string! eventHandlerName) -> void
19+
*REMOVED*Microsoft.AspNetCore.Components.RouteData.RouteData(System.Type! pageType, System.Collections.Generic.IReadOnlyDictionary<string!, object!>! routeValues) -> void
20+
*REMOVED*Microsoft.AspNetCore.Components.RouteData.RouteValues.get -> System.Collections.Generic.IReadOnlyDictionary<string!, object!>!
21+
Microsoft.AspNetCore.Components.RouteData.RouteData(System.Type! pageType, System.Collections.Generic.IReadOnlyDictionary<string!, object?>! routeValues) -> void
22+
Microsoft.AspNetCore.Components.RouteData.RouteValues.get -> System.Collections.Generic.IReadOnlyDictionary<string!, object?>!
23+
Microsoft.AspNetCore.Components.Routing.IRoutingStateProvider
24+
Microsoft.AspNetCore.Components.Routing.IRoutingStateProvider.RouteData.get -> Microsoft.AspNetCore.Components.RouteData?
1925
Microsoft.AspNetCore.Components.Routing.IScrollToLocationHash
2026
Microsoft.AspNetCore.Components.Routing.IScrollToLocationHash.RefreshScrollPositionForHash(string! locationAbsolute) -> System.Threading.Tasks.Task!
2127
Microsoft.AspNetCore.Components.Rendering.ComponentState
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.AspNetCore.Components.Routing;
5+
6+
/// <summary>
7+
/// Provides RouteData
8+
/// </summary>
9+
public interface IRoutingStateProvider
10+
{
11+
/// <summary>
12+
/// Gets RouteData
13+
/// </summary>
14+
public RouteData? RouteData { get; }
15+
}

src/Components/Components/src/Routing/RouteData.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public sealed class RouteData
1717
/// </summary>
1818
/// <param name="pageType">The type of the page matching the route, which must implement <see cref="IComponent"/>.</param>
1919
/// <param name="routeValues">The route parameter values extracted from the matched route.</param>
20-
public RouteData([DynamicallyAccessedMembers(Component)] Type pageType, IReadOnlyDictionary<string, object> routeValues)
20+
public RouteData([DynamicallyAccessedMembers(Component)] Type pageType, IReadOnlyDictionary<string, object?> routeValues)
2121
{
2222
ArgumentNullException.ThrowIfNull(pageType);
2323

@@ -39,5 +39,5 @@ public RouteData([DynamicallyAccessedMembers(Component)] Type pageType, IReadOnl
3939
/// <summary>
4040
/// Gets route parameter values extracted from the matched route.
4141
/// </summary>
42-
public IReadOnlyDictionary<string, object> RouteValues { get; }
42+
public IReadOnlyDictionary<string, object?> RouteValues { get; }
4343
}

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Runtime.ExceptionServices;
99
using Microsoft.AspNetCore.Components.HotReload;
1010
using Microsoft.Extensions.Logging;
11+
using Microsoft.Extensions.DependencyInjection;
1112

1213
namespace Microsoft.AspNetCore.Components.Routing;
1314

@@ -45,6 +46,10 @@ static readonly IReadOnlyDictionary<string, object> _emptyParametersDictionary
4546

4647
[Inject] private ILoggerFactory LoggerFactory { get; set; }
4748

49+
[Inject] IServiceProvider ServiceProvider { get; set; }
50+
51+
private IRoutingStateProvider? RoutingStateProvider { get; set; }
52+
4853
/// <summary>
4954
/// Gets or sets the assembly that should be searched for components matching the URI.
5055
/// </summary>
@@ -99,6 +104,7 @@ public void Attach(RenderHandle renderHandle)
99104
_baseUri = NavigationManager.BaseUri;
100105
_locationAbsolute = NavigationManager.Uri;
101106
NavigationManager.LocationChanged += OnLocationChanged;
107+
RoutingStateProvider = ServiceProvider.GetService<IRoutingStateProvider>();
102108

103109
if (HotReloadManager.Default.MetadataUpdateSupported)
104110
{
@@ -192,10 +198,21 @@ internal virtual void Refresh(bool isNavigationIntercepted)
192198
return;
193199
}
194200

195-
RefreshRouteTable();
196-
197201
var locationPath = NavigationManager.ToBaseRelativePath(_locationAbsolute);
198202
locationPath = TrimQueryOrHash(locationPath);
203+
204+
// In order to avoid routing twice we check for RouteData
205+
if (RoutingStateProvider?.RouteData is { } endpointRouteData)
206+
{
207+
Log.NavigatingToComponent(_logger, endpointRouteData.PageType, locationPath, _baseUri);
208+
209+
_renderHandle.Render(Found(endpointRouteData));
210+
211+
return;
212+
}
213+
214+
RefreshRouteTable();
215+
199216
var context = new RouteContext(locationPath);
200217
Routes.Route(context);
201218

src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSource.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@ private void UpdateEndpoints()
7777
{
7878
var endpoints = new List<Endpoint>();
7979
var context = _builder.Build();
80-
foreach (var definitions in context.Pages)
80+
foreach (var definition in context.Pages)
8181
{
82-
_factory.AddEndpoints(endpoints, definitions, _conventions, _finallyConventions);
82+
_factory.AddEndpoints(endpoints, typeof(TRootComponent), definition, _conventions, _finallyConventions);
8383
}
8484

8585
_endpoints = endpoints;

src/Components/Endpoints/src/Builder/RazorComponentEndpointFactory.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ internal class RazorComponentEndpointFactory
1616
internal void AddEndpoints(
1717
#pragma warning restore CA1822 // It's a singleton
1818
List<Endpoint> endpoints,
19+
Type rootComponent,
1920
PageDefinition pageDefinition,
2021
IReadOnlyList<Action<EndpointBuilder>> conventions,
2122
IReadOnlyList<Action<EndpointBuilder>> finallyConventions)
@@ -38,6 +39,7 @@ internal void AddEndpoints(
3839
builder.Metadata.Add(new SuppressLinkGenerationMetadata());
3940
builder.Metadata.Add(HttpMethodsMetadata);
4041
builder.Metadata.Add(new ComponentTypeMetadata(pageDefinition.Type));
42+
builder.Metadata.Add(new RootComponentMetadata(rootComponent));
4143

4244
foreach (var convention in conventions)
4345
{
@@ -55,7 +57,7 @@ internal void AddEndpoints(
5557
// The display name is for debug purposes by endpoint routing.
5658
builder.DisplayName = $"{builder.RoutePattern.RawText} ({pageDefinition.DisplayName})";
5759

58-
builder.RequestDelegate = httpContext => new RazorComponentEndpointInvoker(httpContext, pageDefinition.Type).RenderComponent();
60+
builder.RequestDelegate = httpContext => new RazorComponentEndpointInvoker(httpContext, rootComponent, pageDefinition.Type).RenderComponent();
5961

6062
endpoints.Add(builder.Build());
6163
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.AspNetCore.Builder;
5+
6+
/// <summary>
7+
/// Metadata that represents the root component associated with an endpoint.
8+
/// </summary>
9+
public class RootComponentMetadata
10+
{
11+
/// <summary>
12+
/// Initializes a new instance of <see cref="RootComponentMetadata"/>.
13+
/// </summary>
14+
/// <param name="rootComponentType">The component type.</param>
15+
public RootComponentMetadata(Type rootComponentType)
16+
{
17+
Type = rootComponentType;
18+
}
19+
20+
/// <summary>
21+
/// Gets the component type.
22+
/// </summary>
23+
public Type Type { get; }
24+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Microsoft.AspNetCore.Components.Routing;
5+
6+
namespace Microsoft.AspNetCore.Components.Endpoints.DependencyInjection;
7+
8+
internal sealed class EndpointRoutingStateProvider : IRoutingStateProvider
9+
{
10+
public RouteData? RouteData { get; internal set; }
11+
}

src/Components/Endpoints/src/DependencyInjection/RazorComponentsServiceCollectionExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Diagnostics.CodeAnalysis;
55
using Microsoft.AspNetCore.Components;
66
using Microsoft.AspNetCore.Components.Endpoints;
7+
using Microsoft.AspNetCore.Components.Endpoints.DependencyInjection;
78
using Microsoft.AspNetCore.Components.Forms;
89
using Microsoft.AspNetCore.Components.Infrastructure;
910
using Microsoft.AspNetCore.Components.Routing;
@@ -50,6 +51,8 @@ public static IRazorComponentsBuilder AddRazorComponents(this IServiceCollection
5051
services.TryAddScoped<PersistentComponentState>(sp => sp.GetRequiredService<ComponentStatePersistenceManager>().State);
5152
services.TryAddScoped<IErrorBoundaryLogger, PrerenderingErrorBoundaryLogger>();
5253
services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<RazorComponentsEndpointsOptions>, RazorComponentsEndpointsDetailedErrorsConfiguration>());
54+
services.TryAddScoped<EndpointRoutingStateProvider>();
55+
services.TryAddScoped<IRoutingStateProvider>(sp => sp.GetRequiredService<EndpointRoutingStateProvider>());
5356

5457
// Form handling
5558
services.TryAddScoped<FormDataProvider, HttpContextFormDataProvider>();

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ Microsoft.AspNetCore.Builder.RazorComponentEndpointConventionBuilder
55
Microsoft.AspNetCore.Builder.RazorComponentEndpointConventionBuilder.Add(System.Action<Microsoft.AspNetCore.Builder.EndpointBuilder!>! convention) -> void
66
Microsoft.AspNetCore.Builder.RazorComponentEndpointConventionBuilder.Finally(System.Action<Microsoft.AspNetCore.Builder.EndpointBuilder!>! finallyConvention) -> void
77
Microsoft.AspNetCore.Builder.RazorComponentsEndpointRouteBuilderExtensions
8+
Microsoft.AspNetCore.Builder.RootComponentMetadata
9+
Microsoft.AspNetCore.Builder.RootComponentMetadata.RootComponentMetadata(System.Type! rootComponentType) -> void
10+
Microsoft.AspNetCore.Builder.RootComponentMetadata.Type.get -> System.Type!
811
Microsoft.AspNetCore.Components.ComponentApplicationBuilder
912
Microsoft.AspNetCore.Components.ComponentApplicationBuilder.Build() -> Microsoft.AspNetCore.Components.RazorComponentApplication!
1013
Microsoft.AspNetCore.Components.ComponentApplicationBuilder.ComponentApplicationBuilder() -> void

0 commit comments

Comments
 (0)