-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Static pages amid global interactivity #54948
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c299094
a7c80f5
de0894c
c034cfa
d43aec4
e117046
8b16d0a
a626347
ab01b76
db1a40b
e3f1ef0
d7ad602
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,4 @@ | ||
#nullable enable | ||
Microsoft.AspNetCore.Components.RouteAttribute.Static.get -> bool | ||
Microsoft.AspNetCore.Components.RouteAttribute.Static.set -> void | ||
virtual Microsoft.AspNetCore.Components.RenderTree.Renderer.ResolveEffectiveRenderMode(System.Type! componentType, int? parentComponentId, Microsoft.AspNetCore.Components.IComponentRenderMode? componentTypeRenderMode, Microsoft.AspNetCore.Components.IComponentRenderMode? callerSpecifiedRenderMode) -> Microsoft.AspNetCore.Components.IComponentRenderMode? |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -83,35 +83,40 @@ static void GetRouteableComponents(List<Type> routeableComponents, Assembly asse | |
|
||
internal static RouteTable Create(List<Type> componentTypes, IServiceProvider serviceProvider) | ||
{ | ||
var templatesByHandler = new Dictionary<Type, string[]>(); | ||
var templatesByHandler = new Dictionary<Type, IReadOnlyList<string>>(); | ||
foreach (var componentType in componentTypes) | ||
{ | ||
// We're deliberately using inherit = false here. | ||
// | ||
// RouteAttribute is defined as non-inherited, because inheriting a route attribute always causes an | ||
// ambiguity. You end up with two components (base class and derived class) with the same route. | ||
var templates = GetTemplates(componentType); | ||
|
||
templatesByHandler.Add(componentType, templates); | ||
// We exclude static routes because this is the interactive router. | ||
var templates = GetTemplates(componentType, includeStaticRoutes: false); | ||
if (templates.Count > 0) | ||
{ | ||
templatesByHandler.Add(componentType, templates); | ||
} | ||
} | ||
return Create(templatesByHandler, serviceProvider); | ||
} | ||
|
||
private static string[] GetTemplates(Type componentType) | ||
private static IReadOnlyList<string> GetTemplates(Type componentType, bool includeStaticRoutes) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replacing |
||
{ | ||
var routeAttributes = componentType.GetCustomAttributes(typeof(RouteAttribute), inherit: false); | ||
var templates = new string[routeAttributes.Length]; | ||
for (var i = 0; i < routeAttributes.Length; i++) | ||
var templates = new List<string>(routeAttributes.Length); | ||
foreach (RouteAttribute routeAttribute in routeAttributes) | ||
{ | ||
var attribute = (RouteAttribute)routeAttributes[i]; | ||
templates[i] = attribute.Template; | ||
if (includeStaticRoutes || !routeAttribute.Static) | ||
{ | ||
templates.Add(routeAttribute.Template); | ||
} | ||
} | ||
|
||
return templates; | ||
} | ||
|
||
[UnconditionalSuppressMessage("Trimming", "IL2067", Justification = "Application code does not get trimmed, and the framework does not define routable components.")] | ||
internal static RouteTable Create(Dictionary<Type, string[]> templatesByHandler, IServiceProvider serviceProvider) | ||
internal static RouteTable Create(Dictionary<Type, IReadOnlyList<string>> templatesByHandler, IServiceProvider serviceProvider) | ||
{ | ||
var routeOptions = Options.Create(new RouteOptions()); | ||
if (!OperatingSystem.IsBrowser() || RegexConstraintSupport.IsEnabled) | ||
|
@@ -141,10 +146,10 @@ internal static RouteTable Create(Dictionary<Type, string[]> templatesByHandler, | |
return new RouteTable(builder.Build()); | ||
} | ||
|
||
private static TemplateGroupInfo ComputeTemplateGroupInfo(string[] templates) | ||
private static TemplateGroupInfo ComputeTemplateGroupInfo(IReadOnlyList<string> templates) | ||
{ | ||
var result = new TemplateGroupInfo(templates); | ||
for (var i = 0; i < templates.Length; i++) | ||
for (var i = 0; i < templates.Count; i++) | ||
{ | ||
var parsedTemplate = RoutePatternParser.Parse(templates[i]); | ||
var parameterNames = GetParameterNames(parsedTemplate); | ||
|
@@ -159,15 +164,15 @@ private static TemplateGroupInfo ComputeTemplateGroupInfo(string[] templates) | |
return result; | ||
} | ||
|
||
private struct TemplateGroupInfo(string[] templates) | ||
private struct TemplateGroupInfo(IReadOnlyCollection<string> templates) | ||
{ | ||
public HashSet<string> AllRouteParameterNames { get; set; } = new(StringComparer.OrdinalIgnoreCase); | ||
public (RoutePattern, HashSet<string>)[] ParsedTemplates { get; set; } = new (RoutePattern, HashSet<string>)[templates.Length]; | ||
public (RoutePattern, HashSet<string>)[] ParsedTemplates { get; set; } = new (RoutePattern, HashSet<string>)[templates.Count]; | ||
} | ||
|
||
internal static InboundRouteEntry CreateEntry([DynamicallyAccessedMembers(Component)] Type pageType, string template) | ||
internal static InboundRouteEntry CreateEntry([DynamicallyAccessedMembers(Component)] Type pageType, string template, bool includeStaticRoutes) | ||
{ | ||
var templates = GetTemplates(pageType); | ||
var templates = GetTemplates(pageType, includeStaticRoutes); | ||
var result = ComputeTemplateGroupInfo(templates); | ||
|
||
RoutePattern? parsedTemplate = null; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,9 +20,25 @@ public ComponentTypeMetadata([DynamicallyAccessedMembers(Component)] Type compon | |
Type = componentType; | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of <see cref="ComponentTypeMetadata"/>. | ||
/// </summary> | ||
/// <param name="componentType">The component type.</param> | ||
/// <param name="isStaticPage">A flag indicating whether the page's route is declared as static.</param> | ||
public ComponentTypeMetadata([DynamicallyAccessedMembers(Component)] Type componentType, bool isStaticPage) | ||
: this(componentType) | ||
{ | ||
IsStaticRoute = isStaticPage; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: We can discuss this in API review, but is there a particular reason for naming the parameter There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah good point. It should probably be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And more broadly as @javiercn has already suggested, the whole name |
||
} | ||
|
||
/// <summary> | ||
/// Gets the component type. | ||
/// </summary> | ||
[DynamicallyAccessedMembers(Component)] | ||
public Type Type { get; } | ||
|
||
/// <summary> | ||
/// Gets a flag indicating whether the page's route is declared as static. | ||
/// </summary> | ||
public bool IsStaticRoute { get; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could go into an entirely separate metadata class but then that's an extra metadata entry we have to look up on every request, and I don't see there being much reason for it. |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
#nullable enable | ||
Microsoft.AspNetCore.Components.Endpoints.ComponentTypeMetadata.ComponentTypeMetadata(System.Type! componentType, bool isStaticPage) -> void | ||
Microsoft.AspNetCore.Components.Endpoints.ComponentTypeMetadata.IsStaticRoute.get -> bool |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,8 +46,14 @@ private async Task RenderComponentCore(HttpContext context) | |
|
||
var endpoint = context.GetEndpoint() ?? throw new InvalidOperationException($"An endpoint must be set on the '{nameof(HttpContext)}'."); | ||
|
||
var componentTypeMetadata = endpoint.Metadata.GetRequiredMetadata<ComponentTypeMetadata>(); | ||
var rootComponent = endpoint.Metadata.GetRequiredMetadata<RootComponentMetadata>().Type; | ||
var pageComponent = endpoint.Metadata.GetRequiredMetadata<ComponentTypeMetadata>().Type; | ||
var pageComponent = componentTypeMetadata.Type; | ||
|
||
if (componentTypeMetadata.IsStaticRoute) | ||
{ | ||
_renderer.SuppressRootComponentRenderModes(); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the bit where we take the endpoint metadata and turn it into a behavior on the renderer. |
||
|
||
Log.BeginRenderRootComponent(_logger, rootComponent.Name, pageComponent.Name); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Conceptually the change here is factoring out the "resolve rendermode" logic into a new
virtual
method on the renderer, so that we can implement the new rule about static pages suppressing callsite rendermodes. Doing so happens to simplifyComponentFactory
quite a bit.