Merge pull request #846 from dotnet-maestro-bot/merge/release/2.2-to-master
[automated] Merge branch 'release/2.2' => 'master'
This commit is contained in:
commit
93dc6dd5f5
|
|
@ -8,10 +8,10 @@ using Microsoft.Extensions.DependencyInjection;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing.LinkGeneration
|
||||
{
|
||||
public class SingleRouteRouteValuesBasedEndpointFinderBenchmark : EndpointRoutingBenchmarkBase
|
||||
public class SingleRouteRouteValuesAddressSchemeBenchmark : EndpointRoutingBenchmarkBase
|
||||
{
|
||||
private IEndpointFinder<RouteValuesAddress> _finder;
|
||||
private TestEndpointFinder _baseFinder;
|
||||
private IEndpointAddressScheme<RouteValuesAddress> _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<IEndpointFinder<RouteValuesAddress>>();
|
||||
_baseFinder = new TestEndpointFinder(Endpoints[0]);
|
||||
_implementation = services.GetRequiredService<IEndpointAddressScheme<RouteValuesAddress>>();
|
||||
_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<int>
|
||||
private class TestAddressScheme : IEndpointAddressScheme<int>
|
||||
{
|
||||
private readonly Endpoint _endpoint;
|
||||
|
||||
public TestEndpointFinder(Endpoint endpoint)
|
||||
public TestAddressScheme(Endpoint endpoint)
|
||||
{
|
||||
_endpoint = endpoint;
|
||||
}
|
||||
|
|
@ -202,7 +202,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
|
||||
private List<RouteEndpoint> GetEndpoints<TAddress>(TAddress address)
|
||||
{
|
||||
var addressingScheme = _serviceProvider.GetRequiredService<IEndpointFinder<TAddress>>();
|
||||
var addressingScheme = _serviceProvider.GetRequiredService<IEndpointAddressScheme<TAddress>>();
|
||||
var endpoints = addressingScheme.FindEndpoints(address).OfType<RouteEndpoint>().ToList();
|
||||
|
||||
if (endpoints.Count == 0)
|
||||
|
|
|
|||
|
|
@ -75,8 +75,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
|
||||
// Link generation related services
|
||||
services.TryAddSingleton<LinkGenerator, DefaultLinkGenerator>();
|
||||
services.TryAddSingleton<IEndpointFinder<string>, EndpointNameEndpointFinder>();
|
||||
services.TryAddSingleton<IEndpointFinder<RouteValuesAddress>, RouteValuesBasedEndpointFinder>();
|
||||
services.TryAddSingleton<IEndpointAddressScheme<string>, EndpointNameAddressScheme>();
|
||||
services.TryAddSingleton<IEndpointAddressScheme<RouteValuesAddress>, RouteValuesAddressScheme>();
|
||||
|
||||
//
|
||||
// Endpoint Selection
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ using Microsoft.AspNetCore.Http;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
internal class EndpointNameEndpointFinder : IEndpointFinder<string>
|
||||
internal class EndpointNameAddressScheme : IEndpointAddressScheme<string>
|
||||
{
|
||||
private readonly DataSourceDependentCache<Dictionary<string, Endpoint[]>> _cache;
|
||||
|
||||
public EndpointNameEndpointFinder(CompositeEndpointDataSource dataSource)
|
||||
public EndpointNameAddressScheme(CompositeEndpointDataSource dataSource)
|
||||
{
|
||||
_cache = new DataSourceDependentCache<Dictionary<string, Endpoint[]>>(dataSource, Initialize);
|
||||
}
|
||||
|
|
@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
|
||||
string GetEndpointName(Endpoint endpoint)
|
||||
{
|
||||
if (endpoint.Metadata.GetMetadata<ISuppressLinkGenerationMetadata>() != null)
|
||||
if (endpoint.Metadata.GetMetadata<ISuppressLinkGenerationMetadata>()?.SuppressLinkGeneration == true)
|
||||
{
|
||||
// Skip anything that's suppressed for linking.
|
||||
return null;
|
||||
|
|
@ -7,13 +7,13 @@ using Microsoft.AspNetCore.Http;
|
|||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a contract to find endpoints based on the supplied lookup information.
|
||||
/// Defines a contract to find endpoints based on the provided address.
|
||||
/// </summary>
|
||||
/// <typeparam name="TAddress">The address type to look up endpoints.</typeparam>
|
||||
public interface IEndpointFinder<TAddress>
|
||||
public interface IEndpointAddressScheme<TAddress>
|
||||
{
|
||||
/// <summary>
|
||||
/// Finds endpoints based on the supplied lookup information.
|
||||
/// Finds endpoints based on the provided <paramref name="address"/>.
|
||||
/// </summary>
|
||||
/// <param name="address">The information used to look up endpoints.</param>
|
||||
/// <returns>A collection of <see cref="Endpoint"/>.</returns>
|
||||
|
|
@ -4,10 +4,14 @@
|
|||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents metadata used during link generation.
|
||||
/// The associated endpoint will not be considered for link generation.
|
||||
/// Represents metadata used during link generation. If <see cref="SuppressLinkGeneration"/> is <c>true</c>
|
||||
/// the associated endpoint will not be used for link generation.
|
||||
/// </summary>
|
||||
public interface ISuppressLinkGenerationMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the assocated endpoint should be used for link generation.
|
||||
/// </summary>
|
||||
bool SuppressLinkGeneration { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -4,10 +4,14 @@
|
|||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// 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 <see cref="SuppressMatching"/> is <c>true</c> the
|
||||
/// associated endpoint will not be considered for URL matching.
|
||||
/// </summary>
|
||||
public interface ISuppressMatchingMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the assocated endpoint should be used for URL matching.
|
||||
/// </summary>
|
||||
bool SuppressMatching { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ISuppressMatchingMetadata>() == null)
|
||||
if (endpoints[i] is RouteEndpoint endpoint && endpoint.Metadata.GetMetadata<ISuppressMatchingMetadata>()?.SuppressMatching == true)
|
||||
{
|
||||
builder.AddEndpoint(endpoint);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ISuppressMatchingMetadata>() == null)
|
||||
if (endpoints[i] is RouteEndpoint endpoint && endpoint.Metadata.GetMetadata<ISuppressMatchingMetadata>()?.SuppressMatching != true)
|
||||
{
|
||||
builder.AddEndpoint(endpoint);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,13 +12,13 @@ using Microsoft.Extensions.Primitives;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
internal class RouteValuesBasedEndpointFinder : IEndpointFinder<RouteValuesAddress>
|
||||
internal class RouteValuesAddressScheme : IEndpointAddressScheme<RouteValuesAddress>
|
||||
{
|
||||
private readonly CompositeEndpointDataSource _dataSource;
|
||||
private LinkGenerationDecisionTree _allMatchesLinkGenerationTree;
|
||||
private IDictionary<string, List<OutboundMatchResult>> _namedMatchResults;
|
||||
|
||||
public RouteValuesBasedEndpointFinder(CompositeEndpointDataSource dataSource)
|
||||
public RouteValuesAddressScheme(CompositeEndpointDataSource dataSource)
|
||||
{
|
||||
_dataSource = dataSource;
|
||||
|
||||
|
|
@ -102,9 +102,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
var endpoints = _dataSource.Endpoints.OfType<RouteEndpoint>();
|
||||
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<ISuppressLinkGenerationMetadata>();
|
||||
if (suppressLinkGeneration != null)
|
||||
if (endpoint.Metadata.GetMetadata<ISuppressLinkGenerationMetadata>()?.SuppressLinkGeneration == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -4,10 +4,14 @@
|
|||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents metadata used during link generation.
|
||||
/// The associated endpoint will not be considered for link generation.
|
||||
/// Represents metadata used during link generation. If <see cref="SuppressLinkGeneration"/> is <c>true</c>
|
||||
/// the associated endpoint will not be used for link generation.
|
||||
/// </summary>
|
||||
public sealed class SuppressLinkGenerationMetadata : ISuppressLinkGenerationMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the assocated endpoint should be used for link generation.
|
||||
/// </summary>
|
||||
public bool SuppressLinkGeneration => true;
|
||||
}
|
||||
}
|
||||
|
|
@ -4,10 +4,14 @@
|
|||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// 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 <see cref="SuppressMatching"/> is <c>true</c> the
|
||||
/// associated endpoint will not be considered for URL matching.
|
||||
/// </summary>
|
||||
public sealed class SuppressMatchingMetadata : ISuppressMatchingMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the assocated endpoint should be used for URL matching.
|
||||
/// </summary>
|
||||
public bool SuppressMatching => true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -599,14 +599,14 @@ namespace Microsoft.AspNetCore.Routing
|
|||
|
||||
protected override void AddAdditionalServices(IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IEndpointFinder<int>, IntEndpointFinder>();
|
||||
services.AddSingleton<IEndpointAddressScheme<int>, IntAddressScheme>();
|
||||
}
|
||||
|
||||
private class IntEndpointFinder : IEndpointFinder<int>
|
||||
private class IntAddressScheme : IEndpointAddressScheme<int>
|
||||
{
|
||||
private readonly CompositeEndpointDataSource _dataSource;
|
||||
|
||||
public IntEndpointFinder(CompositeEndpointDataSource dataSource)
|
||||
public IntAddressScheme(CompositeEndpointDataSource dataSource)
|
||||
{
|
||||
_dataSource = dataSource;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<InvalidOperationException>(() => finder.FindEndpoints("any name"));
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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<TestMatcher>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<RouteEndpoint>(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<RouteEndpoint>(namedMatches[0].Match.Entry.Data));
|
||||
Assert.Same(endpoint3, Assert.IsType<RouteEndpoint>(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<RouteEndpoint>(namedMatches[0].Match.Entry.Data));
|
||||
Assert.Same(endpoint3, Assert.IsType<RouteEndpoint>(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<RouteEndpoint>(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<RouteEndpoint>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue