diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/SingleRouteRouteValuesBasedEndpointFinderBenchmark.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/SingleRouteRouteValuesAddressSchemeBenchmark.cs similarity index 72% rename from benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/SingleRouteRouteValuesBasedEndpointFinderBenchmark.cs rename to benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/SingleRouteRouteValuesAddressSchemeBenchmark.cs index 46241462ac..d6295f890e 100644 --- a/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/SingleRouteRouteValuesBasedEndpointFinderBenchmark.cs +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/SingleRouteRouteValuesAddressSchemeBenchmark.cs @@ -8,10 +8,10 @@ using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNetCore.Routing.LinkGeneration { - public class SingleRouteRouteValuesBasedEndpointFinderBenchmark : EndpointRoutingBenchmarkBase + public class SingleRouteRouteValuesAddressSchemeBenchmark : EndpointRoutingBenchmarkBase { - private IEndpointFinder _finder; - private TestEndpointFinder _baseFinder; + private IEndpointAddressScheme _implementation; + private TestAddressScheme _baseline; private (HttpContext HttpContext, RouteValueDictionary AmbientValues) _requestContext; [GlobalSetup] @@ -23,8 +23,8 @@ namespace Microsoft.AspNetCore.Routing.LinkGeneration SetupEndpoints(CreateEndpoint(template, defaults, requiredValues: requiredValues, routeName: "ProductDetails")); var services = CreateServices(); - _finder = services.GetRequiredService>(); - _baseFinder = new TestEndpointFinder(Endpoints[0]); + _implementation = services.GetRequiredService>(); + _baseline = new TestAddressScheme(Endpoints[0]); _requestContext = CreateCurrentRequestContext(); } @@ -32,13 +32,13 @@ namespace Microsoft.AspNetCore.Routing.LinkGeneration [Benchmark(Baseline = true)] public void Baseline() { - var actual = _baseFinder.FindEndpoints(address: 0); + var actual = _baseline.FindEndpoints(address: 0); } [Benchmark] public void RouteValues() { - var actual = _finder.FindEndpoints(new RouteValuesAddress + var actual = _implementation.FindEndpoints(new RouteValuesAddress { AmbientValues = _requestContext.AmbientValues, ExplicitValues = new RouteValueDictionary(new { controller = "Products", action = "Details" }), @@ -49,18 +49,18 @@ namespace Microsoft.AspNetCore.Routing.LinkGeneration [Benchmark] public void RouteName() { - var actual = _finder.FindEndpoints(new RouteValuesAddress + var actual = _implementation.FindEndpoints(new RouteValuesAddress { AmbientValues = _requestContext.AmbientValues, RouteName = "ProductDetails" }); } - private class TestEndpointFinder : IEndpointFinder + private class TestAddressScheme : IEndpointAddressScheme { private readonly Endpoint _endpoint; - public TestEndpointFinder(Endpoint endpoint) + public TestAddressScheme(Endpoint endpoint) { _endpoint = endpoint; } diff --git a/src/Microsoft.AspNetCore.Routing/DefaultLinkGenerator.cs b/src/Microsoft.AspNetCore.Routing/DefaultLinkGenerator.cs index c652be90be..bc4b3f85b5 100644 --- a/src/Microsoft.AspNetCore.Routing/DefaultLinkGenerator.cs +++ b/src/Microsoft.AspNetCore.Routing/DefaultLinkGenerator.cs @@ -202,7 +202,7 @@ namespace Microsoft.AspNetCore.Routing private List GetEndpoints(TAddress address) { - var addressingScheme = _serviceProvider.GetRequiredService>(); + var addressingScheme = _serviceProvider.GetRequiredService>(); var endpoints = addressingScheme.FindEndpoints(address).OfType().ToList(); if (endpoints.Count == 0) diff --git a/src/Microsoft.AspNetCore.Routing/DependencyInjection/RoutingServiceCollectionExtensions.cs b/src/Microsoft.AspNetCore.Routing/DependencyInjection/RoutingServiceCollectionExtensions.cs index 4db8249104..f79893f85a 100644 --- a/src/Microsoft.AspNetCore.Routing/DependencyInjection/RoutingServiceCollectionExtensions.cs +++ b/src/Microsoft.AspNetCore.Routing/DependencyInjection/RoutingServiceCollectionExtensions.cs @@ -75,8 +75,8 @@ namespace Microsoft.Extensions.DependencyInjection // Link generation related services services.TryAddSingleton(); - services.TryAddSingleton, EndpointNameEndpointFinder>(); - services.TryAddSingleton, RouteValuesBasedEndpointFinder>(); + services.TryAddSingleton, EndpointNameAddressScheme>(); + services.TryAddSingleton, RouteValuesAddressScheme>(); // // Endpoint Selection diff --git a/src/Microsoft.AspNetCore.Routing/EndpointNameEndpointFinder.cs b/src/Microsoft.AspNetCore.Routing/EndpointNameAddressScheme.cs similarity index 94% rename from src/Microsoft.AspNetCore.Routing/EndpointNameEndpointFinder.cs rename to src/Microsoft.AspNetCore.Routing/EndpointNameAddressScheme.cs index 404ffde04b..2716de36e8 100644 --- a/src/Microsoft.AspNetCore.Routing/EndpointNameEndpointFinder.cs +++ b/src/Microsoft.AspNetCore.Routing/EndpointNameAddressScheme.cs @@ -9,11 +9,11 @@ using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Routing { - internal class EndpointNameEndpointFinder : IEndpointFinder + internal class EndpointNameAddressScheme : IEndpointAddressScheme { private readonly DataSourceDependentCache> _cache; - public EndpointNameEndpointFinder(CompositeEndpointDataSource dataSource) + public EndpointNameAddressScheme(CompositeEndpointDataSource dataSource) { _cache = new DataSourceDependentCache>(dataSource, Initialize); } @@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.Routing string GetEndpointName(Endpoint endpoint) { - if (endpoint.Metadata.GetMetadata() != null) + if (endpoint.Metadata.GetMetadata()?.SuppressLinkGeneration == true) { // Skip anything that's suppressed for linking. return null; diff --git a/src/Microsoft.AspNetCore.Routing/IEndpointFinderOfT.cs b/src/Microsoft.AspNetCore.Routing/IEndpointAddressScheme.cs similarity index 76% rename from src/Microsoft.AspNetCore.Routing/IEndpointFinderOfT.cs rename to src/Microsoft.AspNetCore.Routing/IEndpointAddressScheme.cs index b40453d678..e6b6a435bc 100644 --- a/src/Microsoft.AspNetCore.Routing/IEndpointFinderOfT.cs +++ b/src/Microsoft.AspNetCore.Routing/IEndpointAddressScheme.cs @@ -7,13 +7,13 @@ using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Routing { /// - /// Defines a contract to find endpoints based on the supplied lookup information. + /// Defines a contract to find endpoints based on the provided address. /// /// The address type to look up endpoints. - public interface IEndpointFinder + public interface IEndpointAddressScheme { /// - /// Finds endpoints based on the supplied lookup information. + /// Finds endpoints based on the provided . /// /// The information used to look up endpoints. /// A collection of . diff --git a/src/Microsoft.AspNetCore.Routing/ISuppressLinkGenerationMetadata.cs b/src/Microsoft.AspNetCore.Routing/ISuppressLinkGenerationMetadata.cs index 4725b1a8c2..3541bad830 100644 --- a/src/Microsoft.AspNetCore.Routing/ISuppressLinkGenerationMetadata.cs +++ b/src/Microsoft.AspNetCore.Routing/ISuppressLinkGenerationMetadata.cs @@ -4,10 +4,14 @@ namespace Microsoft.AspNetCore.Routing { /// - /// Represents metadata used during link generation. - /// The associated endpoint will not be considered for link generation. + /// Represents metadata used during link generation. If is true + /// the associated endpoint will not be used for link generation. /// public interface ISuppressLinkGenerationMetadata { + /// + /// Gets a value indicating whether the assocated endpoint should be used for link generation. + /// + bool SuppressLinkGeneration { get; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Routing/ISuppressMatchingMetadata.cs b/src/Microsoft.AspNetCore.Routing/ISuppressMatchingMetadata.cs index 8baa0e0a77..d5acffb744 100644 --- a/src/Microsoft.AspNetCore.Routing/ISuppressMatchingMetadata.cs +++ b/src/Microsoft.AspNetCore.Routing/ISuppressMatchingMetadata.cs @@ -4,10 +4,14 @@ namespace Microsoft.AspNetCore.Routing { /// - /// Metadata used to prevent URL matching. The associated endpoint will not be - /// considered URL matching for incoming requests. + /// Metadata used to prevent URL matching. If is true the + /// associated endpoint will not be considered for URL matching. /// public interface ISuppressMatchingMetadata { + /// + /// Gets a value indicating whether the assocated endpoint should be used for URL matching. + /// + bool SuppressMatching { get; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Routing/Internal/DfaGraphWriter.cs b/src/Microsoft.AspNetCore.Routing/Internal/DfaGraphWriter.cs index ed1c3f2d9f..033a56c7ee 100644 --- a/src/Microsoft.AspNetCore.Routing/Internal/DfaGraphWriter.cs +++ b/src/Microsoft.AspNetCore.Routing/Internal/DfaGraphWriter.cs @@ -38,7 +38,7 @@ namespace Microsoft.AspNetCore.Routing.Internal var endpoints = dataSource.Endpoints; for (var i = 0; i < endpoints.Count; i++) { - if (endpoints[i] is RouteEndpoint endpoint && endpoint.Metadata.GetMetadata() == null) + if (endpoints[i] is RouteEndpoint endpoint && endpoint.Metadata.GetMetadata()?.SuppressMatching == true) { builder.AddEndpoint(endpoint); } diff --git a/src/Microsoft.AspNetCore.Routing/Matching/DataSourceDependentMatcher.cs b/src/Microsoft.AspNetCore.Routing/Matching/DataSourceDependentMatcher.cs index fec90b0d64..89c11090ba 100644 --- a/src/Microsoft.AspNetCore.Routing/Matching/DataSourceDependentMatcher.cs +++ b/src/Microsoft.AspNetCore.Routing/Matching/DataSourceDependentMatcher.cs @@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Routing.Matching // By design we only look at RouteEndpoint here. It's possible to // register other endpoint types, which are non-routable, and it's // ok that we won't route to them. - if (endpoints[i] is RouteEndpoint endpoint && endpoint.Metadata.GetMetadata() == null) + if (endpoints[i] is RouteEndpoint endpoint && endpoint.Metadata.GetMetadata()?.SuppressMatching != true) { builder.AddEndpoint(endpoint); } diff --git a/src/Microsoft.AspNetCore.Routing/RouteValuesBasedEndpointFinder.cs b/src/Microsoft.AspNetCore.Routing/RouteValuesAddressScheme.cs similarity index 93% rename from src/Microsoft.AspNetCore.Routing/RouteValuesBasedEndpointFinder.cs rename to src/Microsoft.AspNetCore.Routing/RouteValuesAddressScheme.cs index 042cda60a6..dc103128d1 100644 --- a/src/Microsoft.AspNetCore.Routing/RouteValuesBasedEndpointFinder.cs +++ b/src/Microsoft.AspNetCore.Routing/RouteValuesAddressScheme.cs @@ -12,13 +12,13 @@ using Microsoft.Extensions.Primitives; namespace Microsoft.AspNetCore.Routing { - internal class RouteValuesBasedEndpointFinder : IEndpointFinder + internal class RouteValuesAddressScheme : IEndpointAddressScheme { private readonly CompositeEndpointDataSource _dataSource; private LinkGenerationDecisionTree _allMatchesLinkGenerationTree; private IDictionary> _namedMatchResults; - public RouteValuesBasedEndpointFinder(CompositeEndpointDataSource dataSource) + public RouteValuesAddressScheme(CompositeEndpointDataSource dataSource) { _dataSource = dataSource; @@ -102,9 +102,7 @@ namespace Microsoft.AspNetCore.Routing var endpoints = _dataSource.Endpoints.OfType(); foreach (var endpoint in endpoints) { - // Do not consider an endpoint for link generation if the following marker metadata is on it - var suppressLinkGeneration = endpoint.Metadata.GetMetadata(); - if (suppressLinkGeneration != null) + if (endpoint.Metadata.GetMetadata()?.SuppressLinkGeneration == true) { continue; } diff --git a/src/Microsoft.AspNetCore.Routing/SuppressLinkGenerationMetadata.cs b/src/Microsoft.AspNetCore.Routing/SuppressLinkGenerationMetadata.cs index 88efd9077f..4ff3e86a9f 100644 --- a/src/Microsoft.AspNetCore.Routing/SuppressLinkGenerationMetadata.cs +++ b/src/Microsoft.AspNetCore.Routing/SuppressLinkGenerationMetadata.cs @@ -4,10 +4,14 @@ namespace Microsoft.AspNetCore.Routing { /// - /// Represents metadata used during link generation. - /// The associated endpoint will not be considered for link generation. + /// Represents metadata used during link generation. If is true + /// the associated endpoint will not be used for link generation. /// public sealed class SuppressLinkGenerationMetadata : ISuppressLinkGenerationMetadata { + /// + /// Gets a value indicating whether the assocated endpoint should be used for link generation. + /// + public bool SuppressLinkGeneration => true; } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Routing/SuppressMatchingMetadata.cs b/src/Microsoft.AspNetCore.Routing/SuppressMatchingMetadata.cs index da7a3bce1d..b7edd60f16 100644 --- a/src/Microsoft.AspNetCore.Routing/SuppressMatchingMetadata.cs +++ b/src/Microsoft.AspNetCore.Routing/SuppressMatchingMetadata.cs @@ -4,10 +4,14 @@ namespace Microsoft.AspNetCore.Routing { /// - /// Metadata used to prevent URL matching. The associated endpoint will not be - /// considered URL matching for incoming requests. + /// Metadata used to prevent URL matching. If is true the + /// associated endpoint will not be considered for URL matching. /// public sealed class SuppressMatchingMetadata : ISuppressMatchingMetadata { + /// + /// Gets a value indicating whether the assocated endpoint should be used for URL matching. + /// + public bool SuppressMatching => true; } } diff --git a/test/Microsoft.AspNetCore.Routing.Tests/DefaultLinkGeneratorTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/DefaultLinkGeneratorTest.cs index 9f0bda2a18..831576c41c 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/DefaultLinkGeneratorTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/DefaultLinkGeneratorTest.cs @@ -599,14 +599,14 @@ namespace Microsoft.AspNetCore.Routing protected override void AddAdditionalServices(IServiceCollection services) { - services.AddSingleton, IntEndpointFinder>(); + services.AddSingleton, IntAddressScheme>(); } - private class IntEndpointFinder : IEndpointFinder + private class IntAddressScheme : IEndpointAddressScheme { private readonly CompositeEndpointDataSource _dataSource; - public IntEndpointFinder(CompositeEndpointDataSource dataSource) + public IntAddressScheme(CompositeEndpointDataSource dataSource) { _dataSource = dataSource; } diff --git a/test/Microsoft.AspNetCore.Routing.Tests/EndpointNameEndpointFinderTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/EndpointNameAddressSchemeTest.cs similarity index 63% rename from test/Microsoft.AspNetCore.Routing.Tests/EndpointNameEndpointFinderTest.cs rename to test/Microsoft.AspNetCore.Routing.Tests/EndpointNameAddressSchemeTest.cs index 9165dce257..d643372063 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/EndpointNameEndpointFinderTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/EndpointNameAddressSchemeTest.cs @@ -9,10 +9,10 @@ using Xunit; namespace Microsoft.AspNetCore.Routing { - public class EndpointNameEndpointFinderTest + public class EndpointNameAddressSchemeTest { [Fact] - public void EndpointFinder_Match_ReturnsMatchingEndpoint() + public void AddressScheme_Match_ReturnsMatchingEndpoint() { // Arrange var endpoint1 = EndpointFactory.CreateRouteEndpoint( @@ -23,10 +23,10 @@ namespace Microsoft.AspNetCore.Routing "/b", metadata: new object[] { new EndpointNameMetadata("name2"), }); - var finder = CreateEndpointFinder(endpoint1, endpoint2); + var addressScheme = CreateAddressScheme(endpoint1, endpoint2); // Act - var endpoints = finder.FindEndpoints("name2"); + var endpoints = addressScheme.FindEndpoints("name2"); // Assert Assert.Collection( @@ -35,41 +35,41 @@ namespace Microsoft.AspNetCore.Routing } [Fact] - public void EndpointFinder_NoMatch_ReturnsEmptyCollection() + public void AddressScheme_NoMatch_ReturnsEmptyCollection() { // Arrange var endpoint = EndpointFactory.CreateRouteEndpoint( "/a", metadata: new object[] { new EndpointNameMetadata("name1"), new SuppressLinkGenerationMetadata(), }); - var finder = CreateEndpointFinder(endpoint); + var addressScheme = CreateAddressScheme(endpoint); // Act - var endpoints = finder.FindEndpoints("name2"); + var endpoints = addressScheme.FindEndpoints("name2"); // Assert Assert.Empty(endpoints); } [Fact] - public void EndpointFinder_NoMatch_CaseSensitive() + public void AddressScheme_NoMatch_CaseSensitive() { // Arrange var endpoint = EndpointFactory.CreateRouteEndpoint( "/a", metadata: new object[] { new EndpointNameMetadata("name1"), new SuppressLinkGenerationMetadata(), }); - var finder = CreateEndpointFinder(endpoint); + var addressScheme = CreateAddressScheme(endpoint); // Act - var endpoints = finder.FindEndpoints("NAME1"); + var endpoints = addressScheme.FindEndpoints("NAME1"); // Assert Assert.Empty(endpoints); } [Fact] - public void EndpointFinder_UpdatesWhenDataSourceChanges() + public void AddressScheme_UpdatesWhenDataSourceChanges() { var endpoint1 = EndpointFactory.CreateRouteEndpoint( "/a", @@ -77,10 +77,10 @@ namespace Microsoft.AspNetCore.Routing var dynamicDataSource = new DynamicEndpointDataSource(new[] { endpoint1 }); // Act 1 - var finder = CreateEndpointFinder(dynamicDataSource); + var addressScheme = CreateAddressScheme(dynamicDataSource); // Assert 1 - var match = Assert.Single(finder.Entries); + var match = Assert.Single(addressScheme.Entries); Assert.Same(endpoint1, match.Value.Single()); // Arrange 2 @@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.Routing // Assert 2 Assert.Collection( - finder.Entries.OrderBy(kvp => kvp.Key), + addressScheme.Entries.OrderBy(kvp => kvp.Key), (m) => { Assert.Same(endpoint1, m.Value.Single()); @@ -106,7 +106,7 @@ namespace Microsoft.AspNetCore.Routing } [Fact] - public void EndpointFinder_IgnoresEndpointsWithSuppressLinkGeneration() + public void AddressScheme_IgnoresEndpointsWithSuppressLinkGeneration() { // Arrange var endpoint = EndpointFactory.CreateRouteEndpoint( @@ -114,14 +114,29 @@ namespace Microsoft.AspNetCore.Routing metadata: new object[] { new EndpointNameMetadata("name1"), new SuppressLinkGenerationMetadata(), }); // Act - var finder = CreateEndpointFinder(endpoint); + var addressScheme = CreateAddressScheme(endpoint); // Assert - Assert.Empty(finder.Entries); + Assert.Empty(addressScheme.Entries); } [Fact] - public void EndpointFinder_IgnoresEndpointsWithoutEndpointName() + public void AddressScheme_UnsuppressedEndpoint_IsUsed() + { + // Arrange + var endpoint = EndpointFactory.CreateRouteEndpoint( + "/a", + metadata: new object[] { new EndpointNameMetadata("name1"), new SuppressLinkGenerationMetadata(), new EncourageLinkGenerationMetadata(), }); + + // Act + var addressScheme = CreateAddressScheme(endpoint); + + // Assert + Assert.Same(endpoint, Assert.Single(Assert.Single(addressScheme.Entries).Value)); + } + + [Fact] + public void AddressScheme_IgnoresEndpointsWithoutEndpointName() { // Arrange var endpoint = EndpointFactory.CreateRouteEndpoint( @@ -129,14 +144,14 @@ namespace Microsoft.AspNetCore.Routing metadata: new object[] { }); // Act - var finder = CreateEndpointFinder(endpoint); + var addressScheme = CreateAddressScheme(endpoint); // Assert - Assert.Empty(finder.Entries); + Assert.Empty(addressScheme.Entries); } [Fact] - public void EndpointFinder_ThrowsExceptionForDuplicateEndpoints() + public void AddressScheme_ThrowsExceptionForDuplicateEndpoints() { // Arrange var endpoints = new Endpoint[] @@ -152,10 +167,10 @@ namespace Microsoft.AspNetCore.Routing EndpointFactory.CreateRouteEndpoint("/f", displayName: "f", metadata: new object[] { new EndpointNameMetadata("name2"), }), }; - var finder = CreateEndpointFinder(endpoints); + var addressScheme = CreateAddressScheme(endpoints); // Act - var ex = Assert.Throws(() => finder.FindEndpoints("any name")); + var ex = Assert.Throws(() => addressScheme.FindEndpoints("any name")); // Assert Assert.Equal(@"The following endpoints with a duplicate endpoint name were found. @@ -171,14 +186,19 @@ f ", ex.Message); } - private EndpointNameEndpointFinder CreateEndpointFinder(params Endpoint[] endpoints) + private EndpointNameAddressScheme CreateAddressScheme(params Endpoint[] endpoints) { - return CreateEndpointFinder(new DefaultEndpointDataSource(endpoints)); + return CreateAddressScheme(new DefaultEndpointDataSource(endpoints)); } - private EndpointNameEndpointFinder CreateEndpointFinder(params EndpointDataSource[] dataSources) + private EndpointNameAddressScheme CreateAddressScheme(params EndpointDataSource[] dataSources) { - return new EndpointNameEndpointFinder(new CompositeEndpointDataSource(dataSources)); + return new EndpointNameAddressScheme(new CompositeEndpointDataSource(dataSources)); + } + + private class EncourageLinkGenerationMetadata : ISuppressLinkGenerationMetadata + { + public bool SuppressLinkGeneration => false; } } } diff --git a/test/Microsoft.AspNetCore.Routing.Tests/LinkGeneratorEndpointNameExtensionsTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/LinkGeneratorEndpointNameExtensionsTest.cs index 85462d314d..c81d134ce6 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/LinkGeneratorEndpointNameExtensionsTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/LinkGeneratorEndpointNameExtensionsTest.cs @@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Routing // Does not cover template processing in detail, those scenarios are validated by TemplateBinderTests // and DefaultLinkGeneratorProcessTemplateTest // - // Does not cover the EndpointNameEndpointFinder in detail. see EndpointNameEndpointFinderTest + // Does not cover the EndpointNameAddressScheme in detail. see EndpointNameAddressSchemeTest public class LinkGeneratorEndpointNameExtensionsTest : LinkGeneratorTestBase { [Fact] diff --git a/test/Microsoft.AspNetCore.Routing.Tests/LinkGeneratorRouteValuesAddressExtensionsTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/LinkGeneratorRouteValuesAddressExtensionsTest.cs index de046fb2b1..548cdadcad 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/LinkGeneratorRouteValuesAddressExtensionsTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/LinkGeneratorRouteValuesAddressExtensionsTest.cs @@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Routing // Does not cover template processing in detail, those scenarios are validated by TemplateBinderTests // and DefaultLinkGeneratorProcessTemplateTest // - // Does not cover the RouteValueBasedEndpointFinder in detail. see RouteValueBasedEndpointFinderTest + // Does not cover the RouteValuesAddressScheme in detail. see RouteValuesAddressSchemeTest public class LinkGeneratorRouteValuesAddressExtensionsTest : LinkGeneratorTestBase { [Fact] diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matching/DataSourceDependentMatcherTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matching/DataSourceDependentMatcherTest.cs index eb41ea1890..3559dd529b 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matching/DataSourceDependentMatcherTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matching/DataSourceDependentMatcherTest.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.TestObjects; using Xunit; @@ -89,6 +88,27 @@ namespace Microsoft.AspNetCore.Routing.Matching Assert.Empty(inner.Endpoints); } + [Fact] + public void Matcher_UnsuppressedEndpoint_IsUsed() + { + // Arrange + var dataSource = new DynamicEndpointDataSource(); + var endpoint = new RouteEndpoint( + TestConstants.EmptyRequestDelegate, + RoutePatternFactory.Parse("/"), + 0, + new EndpointMetadataCollection(new SuppressMatchingMetadata(), new EncourageMatchingMetadata()), + "test"); + dataSource.AddEndpoint(endpoint); + + // Act + var matcher = new DataSourceDependentMatcher(dataSource, TestMatcherBuilder.Create); + + // Assert + var inner = Assert.IsType(matcher.CurrentMatcher); + Assert.Same(endpoint, Assert.Single(inner.Endpoints)); + } + [Fact] public void Cache_Reinitializes_WhenDataSourceChanges() { @@ -139,5 +159,10 @@ namespace Microsoft.AspNetCore.Routing.Matching throw new NotImplementedException(); } } + + private class EncourageMatchingMetadata : ISuppressMatchingMetadata + { + public bool SuppressMatching => false; + } } } diff --git a/test/Microsoft.AspNetCore.Routing.Tests/RouteValueBasedEndpointFinderTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/RouteValuesAddressSchemeTest.cs similarity index 71% rename from test/Microsoft.AspNetCore.Routing.Tests/RouteValueBasedEndpointFinderTest.cs rename to test/Microsoft.AspNetCore.Routing.Tests/RouteValuesAddressSchemeTest.cs index 70c66b052e..9020939218 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/RouteValueBasedEndpointFinderTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/RouteValuesAddressSchemeTest.cs @@ -12,7 +12,7 @@ using Xunit; namespace Microsoft.AspNetCore.Routing { - public class RouteValueBasedEndpointFinderTest + public class RouteValuesAddressSchemeTest { [Fact] public void GetOutboundMatches_GetsNamedMatchesFor_EndpointsHaving_IRouteNameMetadata() @@ -22,13 +22,13 @@ namespace Microsoft.AspNetCore.Routing var endpoint2 = CreateEndpoint("/a", routeName: "named"); // Act - var finder = CreateEndpointFinder(endpoint1, endpoint2); + var addressScheme = CreateAddressScheme(endpoint1, endpoint2); // Assert - Assert.NotNull(finder.AllMatches); - Assert.Equal(2, finder.AllMatches.Count()); - Assert.NotNull(finder.NamedMatches); - Assert.True(finder.NamedMatches.TryGetValue("named", out var namedMatches)); + Assert.NotNull(addressScheme.AllMatches); + Assert.Equal(2, addressScheme.AllMatches.Count()); + Assert.NotNull(addressScheme.NamedMatches); + Assert.True(addressScheme.NamedMatches.TryGetValue("named", out var namedMatches)); var namedMatch = Assert.Single(namedMatches); var actual = Assert.IsType(namedMatch.Match.Entry.Data); Assert.Same(endpoint2, actual); @@ -43,13 +43,13 @@ namespace Microsoft.AspNetCore.Routing var endpoint3 = CreateEndpoint("/b", routeName: "named"); // Act - var finder = CreateEndpointFinder(endpoint1, endpoint2, endpoint3); + var addressScheme = CreateAddressScheme(endpoint1, endpoint2, endpoint3); // Assert - Assert.NotNull(finder.AllMatches); - Assert.Equal(3, finder.AllMatches.Count()); - Assert.NotNull(finder.NamedMatches); - Assert.True(finder.NamedMatches.TryGetValue("named", out var namedMatches)); + Assert.NotNull(addressScheme.AllMatches); + Assert.Equal(3, addressScheme.AllMatches.Count()); + Assert.NotNull(addressScheme.NamedMatches); + Assert.True(addressScheme.NamedMatches.TryGetValue("named", out var namedMatches)); Assert.Equal(2, namedMatches.Count); Assert.Same(endpoint2, Assert.IsType(namedMatches[0].Match.Entry.Data)); Assert.Same(endpoint3, Assert.IsType(namedMatches[1].Match.Entry.Data)); @@ -64,13 +64,13 @@ namespace Microsoft.AspNetCore.Routing var endpoint3 = CreateEndpoint("/b", routeName: "NaMed"); // Act - var finder = CreateEndpointFinder(endpoint1, endpoint2, endpoint3); + var addressScheme = CreateAddressScheme(endpoint1, endpoint2, endpoint3); // Assert - Assert.NotNull(finder.AllMatches); - Assert.Equal(3, finder.AllMatches.Count()); - Assert.NotNull(finder.NamedMatches); - Assert.True(finder.NamedMatches.TryGetValue("named", out var namedMatches)); + Assert.NotNull(addressScheme.AllMatches); + Assert.Equal(3, addressScheme.AllMatches.Count()); + Assert.NotNull(addressScheme.NamedMatches); + Assert.True(addressScheme.NamedMatches.TryGetValue("named", out var namedMatches)); Assert.Equal(2, namedMatches.Count); Assert.Same(endpoint2, Assert.IsType(namedMatches[0].Match.Entry.Data)); Assert.Same(endpoint3, Assert.IsType(namedMatches[1].Match.Entry.Data)); @@ -84,11 +84,11 @@ namespace Microsoft.AspNetCore.Routing var dynamicDataSource = new DynamicEndpointDataSource(new[] { endpoint1 }); // Act 1 - var finder = new CustomRouteValuesBasedEndpointFinder(new CompositeEndpointDataSource(new[] { dynamicDataSource })); + var addressScheme = new CustomRouteValuesBasedAddressScheme(new CompositeEndpointDataSource(new[] { dynamicDataSource })); // Assert 1 - Assert.NotNull(finder.AllMatches); - var match = Assert.Single(finder.AllMatches); + Assert.NotNull(addressScheme.AllMatches); + var match = Assert.Single(addressScheme.AllMatches); var actual = Assert.IsType(match.Entry.Data); Assert.Same(endpoint1, actual); @@ -114,9 +114,9 @@ namespace Microsoft.AspNetCore.Routing dynamicDataSource.AddEndpoint(endpoint4); // Assert 3 - Assert.NotNull(finder.AllMatches); + Assert.NotNull(addressScheme.AllMatches); Assert.Collection( - finder.AllMatches, + addressScheme.AllMatches, (m) => { actual = Assert.IsType(m.Entry.Data); @@ -148,10 +148,10 @@ namespace Microsoft.AspNetCore.Routing defaults: new { controller = "Orders", action = "GetById" }, requiredValues: new { controller = "Orders", action = "GetById" }, routeName: "OrdersApi"); - var finder = CreateEndpointFinder(expected); + var addressScheme = CreateAddressScheme(expected); // Act - var foundEndpoints = finder.FindEndpoints( + var foundEndpoints = addressScheme.FindEndpoints( new RouteValuesAddress { ExplicitValues = new RouteValueDictionary(new { id = 10 }), @@ -167,7 +167,7 @@ namespace Microsoft.AspNetCore.Routing [Fact] public void FindEndpoints_AlwaysReturnsEndpointsByRouteName_IgnoringMissingRequiredParameterValues() { - // Here 'id' is the required value. The endpoint finder would always return an endpoint by looking up + // Here 'id' is the required value. The endpoint addressScheme would always return an endpoint by looking up // name only. Its the link generator which uses these endpoints finally to generate a link or not // based on the required parameter values being present or not. @@ -177,10 +177,10 @@ namespace Microsoft.AspNetCore.Routing defaults: new { controller = "Orders", action = "GetById" }, requiredValues: new { controller = "Orders", action = "GetById" }, routeName: "OrdersApi"); - var finder = CreateEndpointFinder(expected); + var addressScheme = CreateAddressScheme(expected); // Act - var foundEndpoints = finder.FindEndpoints( + var foundEndpoints = addressScheme.FindEndpoints( new RouteValuesAddress { ExplicitValues = new RouteValueDictionary(), @@ -202,20 +202,35 @@ namespace Microsoft.AspNetCore.Routing metadataCollection: new EndpointMetadataCollection(new[] { new SuppressLinkGenerationMetadata() })); // Act - var finder = CreateEndpointFinder(endpoint); + var addressScheme = CreateAddressScheme(endpoint); // Assert - Assert.Empty(finder.AllMatches); + Assert.Empty(addressScheme.AllMatches); } - private CustomRouteValuesBasedEndpointFinder CreateEndpointFinder(params Endpoint[] endpoints) + [Fact] + public void AddressScheme_UnsuppressedEndpoint_IsUsed() { - return CreateEndpointFinder(new DefaultEndpointDataSource(endpoints)); + // Arrange + var endpoint = EndpointFactory.CreateRouteEndpoint( + "/a", + metadata: new object[] { new SuppressLinkGenerationMetadata(), new EncourageLinkGenerationMetadata(), }); + + // Act + var addressScheme = CreateAddressScheme(endpoint); + + // Assert + Assert.Same(endpoint, Assert.Single(addressScheme.AllMatches).Entry.Data); } - private CustomRouteValuesBasedEndpointFinder CreateEndpointFinder(params EndpointDataSource[] dataSources) + private CustomRouteValuesBasedAddressScheme CreateAddressScheme(params Endpoint[] endpoints) { - return new CustomRouteValuesBasedEndpointFinder(new CompositeEndpointDataSource(dataSources)); + return CreateAddressScheme(new DefaultEndpointDataSource(endpoints)); + } + + private CustomRouteValuesBasedAddressScheme CreateAddressScheme(params EndpointDataSource[] dataSources) + { + return new CustomRouteValuesBasedAddressScheme(new CompositeEndpointDataSource(dataSources)); } private RouteEndpoint CreateEndpoint( @@ -244,9 +259,9 @@ namespace Microsoft.AspNetCore.Routing null); } - private class CustomRouteValuesBasedEndpointFinder : RouteValuesBasedEndpointFinder + private class CustomRouteValuesBasedAddressScheme : RouteValuesAddressScheme { - public CustomRouteValuesBasedEndpointFinder(CompositeEndpointDataSource dataSource) + public CustomRouteValuesBasedAddressScheme(CompositeEndpointDataSource dataSource) : base(dataSource) { } @@ -263,5 +278,10 @@ namespace Microsoft.AspNetCore.Routing return matches; } } + + private class EncourageLinkGenerationMetadata : ISuppressLinkGenerationMetadata + { + public bool SuppressLinkGeneration => false; + } } }