diff --git a/benchmarkapps/Benchmarks/StartupUsingDispatcher.cs b/benchmarkapps/Benchmarks/StartupUsingDispatcher.cs index f26c36c132..153b6e42cc 100644 --- a/benchmarkapps/Benchmarks/StartupUsingDispatcher.cs +++ b/benchmarkapps/Benchmarks/StartupUsingDispatcher.cs @@ -32,11 +32,11 @@ namespace Benchmarks return response.Body.WriteAsync(_helloWorldPayload, 0, payloadLength); }, template: "/plaintext", - values: new { }, + defaults: new RouteValueDictionary(), + requiredValues: new RouteValueDictionary(), order: 0, metadata: EndpointMetadataCollection.Empty, - displayName: "Plaintext", - address: null), + displayName: "Plaintext"), })); }); } diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matchers/MatcherBenchmarkBase.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matchers/MatcherBenchmarkBase.cs index a9853d13c3..a2032b3c48 100644 --- a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matchers/MatcherBenchmarkBase.cs +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matchers/MatcherBenchmarkBase.cs @@ -28,11 +28,11 @@ namespace Microsoft.AspNetCore.Routing.Matchers return new MatcherEndpoint( (next) => (context) => Task.CompletedTask, template, - new { }, + new RouteValueDictionary(), + new RouteValueDictionary(), 0, EndpointMetadataCollection.Empty, - template, - address: null); + template); } internal static int[] SampleRequests(int endpointCount, int count) diff --git a/samples/DispatcherSample.Web/Startup.cs b/samples/DispatcherSample.Web/Startup.cs index 5b8103640c..eca7ab2235 100644 --- a/samples/DispatcherSample.Web/Startup.cs +++ b/samples/DispatcherSample.Web/Startup.cs @@ -31,7 +31,7 @@ namespace DispatcherSample.Web response.ContentLength = payloadLength; return response.Body.WriteAsync(_homePayload, 0, payloadLength); }, - "/", new { }, 0, EndpointMetadataCollection.Empty, "Home", address: null), + "/", new RouteValueDictionary(), new RouteValueDictionary(), 0, EndpointMetadataCollection.Empty, "Home"), new MatcherEndpoint((next) => (httpContext) => { var response = httpContext.Response; @@ -41,7 +41,7 @@ namespace DispatcherSample.Web response.ContentLength = payloadLength; return response.Body.WriteAsync(_helloWorldPayload, 0, payloadLength); }, - "/plaintext", new { }, 0, EndpointMetadataCollection.Empty, "Plaintext", address: null), + "/plaintext", new RouteValueDictionary(), new RouteValueDictionary(), 0, EndpointMetadataCollection.Empty, "Plaintext"), })); }); } diff --git a/src/Microsoft.AspNetCore.Routing.Abstractions/Endpoint.cs b/src/Microsoft.AspNetCore.Routing.Abstractions/Endpoint.cs index da138e4014..f58ba517e9 100644 --- a/src/Microsoft.AspNetCore.Routing.Abstractions/Endpoint.cs +++ b/src/Microsoft.AspNetCore.Routing.Abstractions/Endpoint.cs @@ -10,19 +10,15 @@ namespace Microsoft.AspNetCore.Routing { protected Endpoint( EndpointMetadataCollection metadata, - string displayName, - Address address) + string displayName) { // All are allowed to be null Metadata = metadata ?? EndpointMetadataCollection.Empty; DisplayName = displayName; - Address = address; } public string DisplayName { get; } public EndpointMetadataCollection Metadata { get; } - - public Address Address { get; } } } diff --git a/src/Microsoft.AspNetCore.Routing.Abstractions/IEndpointFinder.cs b/src/Microsoft.AspNetCore.Routing.Abstractions/IEndpointFinderOfT.cs similarity index 70% rename from src/Microsoft.AspNetCore.Routing.Abstractions/IEndpointFinder.cs rename to src/Microsoft.AspNetCore.Routing.Abstractions/IEndpointFinderOfT.cs index e799cacbd9..ba450f1c7b 100644 --- a/src/Microsoft.AspNetCore.Routing.Abstractions/IEndpointFinder.cs +++ b/src/Microsoft.AspNetCore.Routing.Abstractions/IEndpointFinderOfT.cs @@ -5,8 +5,8 @@ using System.Collections.Generic; namespace Microsoft.AspNetCore.Routing { - public interface IEndpointFinder + public interface IEndpointFinder { - IEnumerable FindEndpoints(Address address); + IEnumerable FindEndpoints(TAddress address); } } diff --git a/src/Microsoft.AspNetCore.Routing.Abstractions/ILinkGenerator.cs b/src/Microsoft.AspNetCore.Routing.Abstractions/ILinkGenerator.cs index 8734108fb4..22f8547e9c 100644 --- a/src/Microsoft.AspNetCore.Routing.Abstractions/ILinkGenerator.cs +++ b/src/Microsoft.AspNetCore.Routing.Abstractions/ILinkGenerator.cs @@ -1,12 +1,21 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Collections.Generic; + namespace Microsoft.AspNetCore.Routing { public interface ILinkGenerator { - bool TryGetLink(LinkGeneratorContext context, out string link); + bool TryGetLink( + IEnumerable endpoints, + RouteValueDictionary explicitValues, + RouteValueDictionary ambientValues, + out string link); - string GetLink(LinkGeneratorContext context); + string GetLink( + IEnumerable endpoints, + RouteValueDictionary explicitValues, + RouteValueDictionary ambientValues); } } diff --git a/src/Microsoft.AspNetCore.Routing.Abstractions/Address.cs b/src/Microsoft.AspNetCore.Routing.Abstractions/INameMetadata.cs similarity index 54% rename from src/Microsoft.AspNetCore.Routing.Abstractions/Address.cs rename to src/Microsoft.AspNetCore.Routing.Abstractions/INameMetadata.cs index 6004d8b9f8..97de9aaa2f 100644 --- a/src/Microsoft.AspNetCore.Routing.Abstractions/Address.cs +++ b/src/Microsoft.AspNetCore.Routing.Abstractions/INameMetadata.cs @@ -1,19 +1,11 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + namespace Microsoft.AspNetCore.Routing { - public class Address + public interface INameMetadata { - public Address() - { - } - - public Address(string name) - { - Name = name; - } - - public string Name { get; set; } + string Name { get; } } } diff --git a/src/Microsoft.AspNetCore.Routing.Abstractions/IRouteNameMetadata.cs b/src/Microsoft.AspNetCore.Routing.Abstractions/IRouteNameMetadata.cs new file mode 100644 index 0000000000..eba962c285 --- /dev/null +++ b/src/Microsoft.AspNetCore.Routing.Abstractions/IRouteNameMetadata.cs @@ -0,0 +1,11 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + + +namespace Microsoft.AspNetCore.Routing +{ + public interface IRouteNameMetadata + { + string Name { get; } + } +} diff --git a/src/Microsoft.AspNetCore.Routing/DefaultEndpointFinder.cs b/src/Microsoft.AspNetCore.Routing/DefaultEndpointFinder.cs deleted file mode 100644 index cf06f56195..0000000000 --- a/src/Microsoft.AspNetCore.Routing/DefaultEndpointFinder.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Routing -{ - internal class DefaultEndpointFinder : IEndpointFinder - { - private readonly CompositeEndpointDataSource _endpointDatasource; - private readonly ILogger _logger; - - public DefaultEndpointFinder( - CompositeEndpointDataSource endpointDataSource, - ILogger logger) - { - _endpointDatasource = endpointDataSource; - _logger = logger; - } - - public IEnumerable FindEndpoints(Address lookupAddress) - { - var allEndpoints = _endpointDatasource.Endpoints; - - if (lookupAddress == null || string.IsNullOrEmpty(lookupAddress.Name)) - { - return allEndpoints; - } - - var endpointsWithAddress = allEndpoints.Where(ep => ep.Address != null); - if (!endpointsWithAddress.Any()) - { - return allEndpoints; - } - - foreach (var endpoint in endpointsWithAddress) - { - if (string.Equals(lookupAddress.Name, endpoint.Address.Name, StringComparison.OrdinalIgnoreCase)) - { - return new[] { endpoint }; - } - } - - _logger.LogDebug( - $"Could not find an endpoint having an address with name '{lookupAddress.Name}'."); - - return Enumerable.Empty(); - } - } -} diff --git a/src/Microsoft.AspNetCore.Routing/DefaultLinkGenerator.cs b/src/Microsoft.AspNetCore.Routing/DefaultLinkGenerator.cs index efa773e6f5..7efe2cedd8 100644 --- a/src/Microsoft.AspNetCore.Routing/DefaultLinkGenerator.cs +++ b/src/Microsoft.AspNetCore.Routing/DefaultLinkGenerator.cs @@ -15,23 +15,23 @@ namespace Microsoft.AspNetCore.Routing { public class DefaultLinkGenerator : ILinkGenerator { - private readonly IEndpointFinder _endpointFinder; private readonly ObjectPool _uriBuildingContextPool; private readonly ILogger _logger; public DefaultLinkGenerator( - IEndpointFinder endpointFinder, ObjectPool uriBuildingContextPool, ILogger logger) { - _endpointFinder = endpointFinder; _uriBuildingContextPool = uriBuildingContextPool; _logger = logger; } - public string GetLink(LinkGeneratorContext context) + public string GetLink( + IEnumerable endpoints, + RouteValueDictionary explicitValues, + RouteValueDictionary ambientValues) { - if (TryGetLink(context, out var link)) + if (TryGetLink(endpoints, explicitValues, ambientValues, out var link)) { return link; } @@ -39,10 +39,12 @@ namespace Microsoft.AspNetCore.Routing throw new InvalidOperationException("Could not find a matching endpoint to generate a link."); } - public bool TryGetLink(LinkGeneratorContext context, out string link) + public bool TryGetLink( + IEnumerable endpoints, + RouteValueDictionary explicitValues, + RouteValueDictionary ambientValues, + out string link) { - var address = context.Address; - var endpoints = _endpointFinder.FindEndpoints(address); link = null; if (endpoints == null) @@ -59,7 +61,7 @@ namespace Microsoft.AspNetCore.Routing foreach (var endpoint in matcherEndpoints) { - link = GetLink(endpoint.ParsedTemlate, endpoint.Values, context); + link = GetLink(endpoint.ParsedTemplate, endpoint.Defaults, explicitValues, ambientValues); if (link != null) { return true; @@ -71,19 +73,17 @@ namespace Microsoft.AspNetCore.Routing private string GetLink( RouteTemplate template, - IReadOnlyDictionary defaultValues, - LinkGeneratorContext context) + RouteValueDictionary defaults, + RouteValueDictionary explicitValues, + RouteValueDictionary ambientValues) { - var defaults = new RouteValueDictionary(defaultValues); var templateBinder = new TemplateBinder( UrlEncoder.Default, _uriBuildingContextPool, template, defaults); - var values = templateBinder.GetValues( - new RouteValueDictionary(context.AmbientValues), - new RouteValueDictionary(context.SuppliedValues)); + var values = templateBinder.GetValues(ambientValues, explicitValues); if (values == null) { // We're missing one of the required values for this route. diff --git a/src/Microsoft.AspNetCore.Routing/DependencyInjection/DispatcherServiceCollectionExtensions.cs b/src/Microsoft.AspNetCore.Routing/DependencyInjection/DispatcherServiceCollectionExtensions.cs index 64f8d4ad81..565fe50cdf 100644 --- a/src/Microsoft.AspNetCore.Routing/DependencyInjection/DispatcherServiceCollectionExtensions.cs +++ b/src/Microsoft.AspNetCore.Routing/DependencyInjection/DispatcherServiceCollectionExtensions.cs @@ -2,8 +2,10 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Reflection; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing.EndpointConstraints; +using Microsoft.AspNetCore.Routing.EndpointFinders; using Microsoft.AspNetCore.Routing.Matchers; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; @@ -35,7 +37,8 @@ namespace Microsoft.Extensions.DependencyInjection services.TryAddSingleton(); // Link generation related services - services.TryAddSingleton(); + services.TryAddSingleton, NameBasedEndpointFinder>(); + services.TryAddSingleton, RouteValuesBasedEndpointFinder>(); services.TryAddSingleton(); // // Endpoint Selection diff --git a/src/Microsoft.AspNetCore.Routing/Internal/LinkGenerationDecisionTree.cs b/src/Microsoft.AspNetCore.Routing/Internal/LinkGenerationDecisionTree.cs index f71e6dd74d..951d6b645c 100644 --- a/src/Microsoft.AspNetCore.Routing/Internal/LinkGenerationDecisionTree.cs +++ b/src/Microsoft.AspNetCore.Routing/Internal/LinkGenerationDecisionTree.cs @@ -20,18 +20,18 @@ namespace Microsoft.AspNetCore.Routing.Internal new OutboundMatchClassifier()); } - public IList GetMatches(VirtualPathContext context) + public IList GetMatches(RouteValueDictionary values, RouteValueDictionary ambientValues) { // Perf: Avoid allocation for List if there aren't any Matches or Criteria if (_root.Matches.Count > 0 || _root.Criteria.Count > 0) { var results = new List(); - Walk(results, context, _root, isFallbackPath: false); + Walk(results, values, ambientValues, _root, isFallbackPath: false); results.Sort(OutboundMatchResultComparer.Instance); return results; } - return null; + return null; } // We need to recursively walk the decision tree based on the provided route data @@ -61,7 +61,8 @@ namespace Microsoft.AspNetCore.Routing.Internal // The decision tree uses a tree data structure to execute these rules across all candidates at once. private void Walk( List results, - VirtualPathContext context, + RouteValueDictionary values, + RouteValueDictionary ambientValues, DecisionTreeNode node, bool isFallbackPath) { @@ -78,12 +79,12 @@ namespace Microsoft.AspNetCore.Routing.Internal var key = criterion.Key; object value; - if (context.Values.TryGetValue(key, out value)) + if (values.TryGetValue(key, out value)) { DecisionTreeNode branch; if (criterion.Branches.TryGetValue(value ?? string.Empty, out branch)) { - Walk(results, context, branch, isFallbackPath); + Walk(results, values, ambientValues, branch, isFallbackPath); } } else @@ -92,18 +93,18 @@ namespace Microsoft.AspNetCore.Routing.Internal // if an ambient value was supplied. The path explored with the empty value is considered // the fallback path. DecisionTreeNode branch; - if (context.AmbientValues.TryGetValue(key, out value) && + if (ambientValues.TryGetValue(key, out value) && !criterion.Branches.Comparer.Equals(value, string.Empty)) { if (criterion.Branches.TryGetValue(value, out branch)) { - Walk(results, context, branch, isFallbackPath); + Walk(results, values, ambientValues, branch, isFallbackPath); } } if (criterion.Branches.TryGetValue(string.Empty, out branch)) { - Walk(results, context, branch, isFallbackPath: true); + Walk(results, values, ambientValues, branch, isFallbackPath: true); } } } @@ -155,7 +156,7 @@ namespace Microsoft.AspNetCore.Routing.Internal } return StringComparer.Ordinal.Compare( - x.Match.Entry.RouteTemplate.TemplateText, + x.Match.Entry.RouteTemplate.TemplateText, y.Match.Entry.RouteTemplate.TemplateText); } } diff --git a/src/Microsoft.AspNetCore.Routing/Matchers/MatcherEndpoint.cs b/src/Microsoft.AspNetCore.Routing/Matchers/MatcherEndpoint.cs index c416bd148e..41b629ef6c 100644 --- a/src/Microsoft.AspNetCore.Routing/Matchers/MatcherEndpoint.cs +++ b/src/Microsoft.AspNetCore.Routing/Matchers/MatcherEndpoint.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing.Template; @@ -19,12 +18,12 @@ namespace Microsoft.AspNetCore.Routing.Matchers public MatcherEndpoint( Func invoker, string template, - object values, + RouteValueDictionary defaults, + RouteValueDictionary requiredValues, int order, EndpointMetadataCollection metadata, - string displayName, - Address address) - : base(metadata, displayName, address) + string displayName) + : base(metadata, displayName) { if (invoker == null) { @@ -37,24 +36,31 @@ namespace Microsoft.AspNetCore.Routing.Matchers } Invoker = invoker; - Template = template; - ParsedTemlate = TemplateParser.Parse(template); - var mergedDefaults = GetDefaults(ParsedTemlate, new RouteValueDictionary(values)); - Values = mergedDefaults; Order = order; + + Template = template; + ParsedTemplate = TemplateParser.Parse(template); + + RequiredValues = requiredValues; + var mergedDefaults = GetDefaults(ParsedTemplate, defaults); + Defaults = mergedDefaults; } public int Order { get; } public Func Invoker { get; } public string Template { get; } - public IReadOnlyDictionary Values { get; } + public RouteValueDictionary Defaults { get; } + + // Values required by an endpoint for it to be successfully matched on link generation + public RouteValueDictionary RequiredValues { get; } // Todo: needs review - public RouteTemplate ParsedTemlate { get; } + public RouteTemplate ParsedTemplate { get; } - private RouteValueDictionary GetDefaults(RouteTemplate parsedTemplate, RouteValueDictionary defaults) + // Merge inline and non inline defaults into one + private RouteValueDictionary GetDefaults(RouteTemplate parsedTemplate, RouteValueDictionary nonInlineDefaults) { - var result = defaults == null ? new RouteValueDictionary() : new RouteValueDictionary(defaults); + var result = nonInlineDefaults == null ? new RouteValueDictionary() : new RouteValueDictionary(nonInlineDefaults); foreach (var parameter in parsedTemplate.Parameters) { diff --git a/src/Microsoft.AspNetCore.Routing/Matchers/TreeMatcher.cs b/src/Microsoft.AspNetCore.Routing/Matchers/TreeMatcher.cs index fe887569ae..8b1795b0f5 100644 --- a/src/Microsoft.AspNetCore.Routing/Matchers/TreeMatcher.cs +++ b/src/Microsoft.AspNetCore.Routing/Matchers/TreeMatcher.cs @@ -100,7 +100,7 @@ namespace Microsoft.AspNetCore.Routing.Matchers { if (feature.Endpoint is MatcherEndpoint endpoint) { - foreach (var kvp in endpoint.Values) + foreach (var kvp in endpoint.Defaults) { if (!feature.Values.ContainsKey(kvp.Key)) { diff --git a/src/Microsoft.AspNetCore.Routing/NameBasedEndpointFinder.cs b/src/Microsoft.AspNetCore.Routing/NameBasedEndpointFinder.cs new file mode 100644 index 0000000000..c7879b19ee --- /dev/null +++ b/src/Microsoft.AspNetCore.Routing/NameBasedEndpointFinder.cs @@ -0,0 +1,46 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Routing.Matchers; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Routing +{ + internal class NameBasedEndpointFinder : IEndpointFinder + { + private readonly CompositeEndpointDataSource _endpointDatasource; + private readonly ILogger _logger; + + public NameBasedEndpointFinder( + CompositeEndpointDataSource endpointDataSource, + ILogger logger) + { + _endpointDatasource = endpointDataSource; + _logger = logger; + } + + public IEnumerable FindEndpoints(string endpointName) + { + if (string.IsNullOrEmpty(endpointName)) + { + return Array.Empty(); + } + + var endpoints = _endpointDatasource.Endpoints.OfType(); + + foreach (var endpoint in endpoints) + { + var nameMetadata = endpoint.Metadata.GetMetadata(); + if (nameMetadata != null && + string.Equals(endpointName, nameMetadata.Name, StringComparison.OrdinalIgnoreCase)) + { + return new[] { endpoint }; + } + } + return Array.Empty(); + } + } +} diff --git a/src/Microsoft.AspNetCore.Routing/RouteValuesBasedEndpointFinder.cs b/src/Microsoft.AspNetCore.Routing/RouteValuesBasedEndpointFinder.cs new file mode 100644 index 0000000000..a926d51cf7 --- /dev/null +++ b/src/Microsoft.AspNetCore.Routing/RouteValuesBasedEndpointFinder.cs @@ -0,0 +1,166 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Routing.EndpointFinders; +using Microsoft.AspNetCore.Routing.Internal; +using Microsoft.AspNetCore.Routing.Matchers; +using Microsoft.AspNetCore.Routing.Template; +using Microsoft.AspNetCore.Routing.Tree; +using Microsoft.Extensions.ObjectPool; + +namespace Microsoft.AspNetCore.Routing +{ + internal class RouteValuesBasedEndpointFinder : IEndpointFinder + { + private readonly CompositeEndpointDataSource _endpointDataSource; + private readonly IInlineConstraintResolver _inlineConstraintResolver; + private readonly ObjectPool _objectPool; + private LinkGenerationDecisionTree _allMatchesLinkGenerationTree; + private IDictionary _namedMatches; + + public RouteValuesBasedEndpointFinder( + CompositeEndpointDataSource endpointDataSource, + ObjectPool objectPool, + IInlineConstraintResolver inlineConstraintResolver) + { + _endpointDataSource = endpointDataSource; + _objectPool = objectPool; + _inlineConstraintResolver = inlineConstraintResolver; + + BuildOutboundMatches(); + } + + public IEnumerable FindEndpoints(RouteValuesBasedEndpointFinderContext context) + { + IEnumerable matchResults = null; + if (string.IsNullOrEmpty(context.RouteName)) + { + matchResults = _allMatchesLinkGenerationTree.GetMatches( + context.ExplicitValues, + context.AmbientValues); + } + else if (_namedMatches.TryGetValue(context.RouteName, out var linkGenerationTree)) + { + matchResults = linkGenerationTree.GetMatches( + context.ExplicitValues, + context.AmbientValues); + } + + if (matchResults == null || !matchResults.Any()) + { + return Array.Empty(); + } + + return matchResults + .Select(matchResult => matchResult.Match) + .Select(match => (MatcherEndpoint)match.Entry.Data); + } + + private void BuildOutboundMatches() + { + var (allOutboundMatches, namedOutboundMatches) = GetOutboundMatches(); + _namedMatches = GetNamedMatches(namedOutboundMatches); + _allMatchesLinkGenerationTree = new LinkGenerationDecisionTree(allOutboundMatches.ToArray()); + } + + private (IEnumerable, IDictionary>) GetOutboundMatches() + { + var allOutboundMatches = new List(); + var namedOutboundMatches = new Dictionary>(StringComparer.OrdinalIgnoreCase); + + var endpoints = _endpointDataSource.Endpoints.OfType(); + foreach (var endpoint in endpoints) + { + var entry = CreateOutboundRouteEntry(endpoint); + + var outboundMatch = new OutboundMatch() { Entry = entry }; + allOutboundMatches.Add(outboundMatch); + + if (string.IsNullOrEmpty(entry.RouteName)) + { + continue; + } + + List matches; + if (!namedOutboundMatches.TryGetValue(entry.RouteName, out matches)) + { + matches = new List(); + namedOutboundMatches.Add(entry.RouteName, matches); + } + matches.Add(outboundMatch); + } + + return (allOutboundMatches, namedOutboundMatches); + } + + private OutboundRouteEntry CreateOutboundRouteEntry(MatcherEndpoint endpoint) + { + var routeNameMetadata = endpoint.Metadata.GetMetadata(); + var entry = new OutboundRouteEntry() + { + Handler = NullRouter.Instance, + Order = endpoint.Order, + Precedence = RoutePrecedence.ComputeOutbound(endpoint.ParsedTemplate), + RequiredLinkValues = endpoint.RequiredValues, + RouteTemplate = endpoint.ParsedTemplate, + Data = endpoint, + RouteName = routeNameMetadata?.Name, + }; + + // TODO: review. These route constriants should be constructed when the endpoint + // is built. This way they can be checked for validity on app startup too + var constraintBuilder = new RouteConstraintBuilder( + _inlineConstraintResolver, + endpoint.ParsedTemplate.TemplateText); + foreach (var parameter in endpoint.ParsedTemplate.Parameters) + { + if (parameter.InlineConstraints != null) + { + if (parameter.IsOptional) + { + constraintBuilder.SetOptional(parameter.Name); + } + + foreach (var constraint in parameter.InlineConstraints) + { + constraintBuilder.AddResolvedConstraint(parameter.Name, constraint.Constraint); + } + } + } + entry.Constraints = constraintBuilder.Build(); + entry.Defaults = endpoint.Defaults; + return entry; + } + + private IDictionary GetNamedMatches( + IDictionary> namedOutboundMatches) + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var namedOutboundMatch in namedOutboundMatches) + { + result.Add(namedOutboundMatch.Key, new LinkGenerationDecisionTree(namedOutboundMatch.Value.ToArray())); + } + return result; + } + + // Used only to hook up link generation, and it doesn't need to do anything. + private class NullRouter : IRouter + { + public static readonly NullRouter Instance = new NullRouter(); + + public VirtualPathData GetVirtualPath(VirtualPathContext context) + { + return null; + } + + public Task RouteAsync(RouteContext context) + { + throw new NotImplementedException(); + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Routing.Abstractions/LinkGeneratorContext.cs b/src/Microsoft.AspNetCore.Routing/RouteValuesBasedEndpointFinderContext.cs similarity index 53% rename from src/Microsoft.AspNetCore.Routing.Abstractions/LinkGeneratorContext.cs rename to src/Microsoft.AspNetCore.Routing/RouteValuesBasedEndpointFinderContext.cs index 90f6eab6b8..ccc64ee5c6 100644 --- a/src/Microsoft.AspNetCore.Routing.Abstractions/LinkGeneratorContext.cs +++ b/src/Microsoft.AspNetCore.Routing/RouteValuesBasedEndpointFinderContext.cs @@ -1,14 +1,14 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -namespace Microsoft.AspNetCore.Routing +namespace Microsoft.AspNetCore.Routing.EndpointFinders { - public class LinkGeneratorContext + public class RouteValuesBasedEndpointFinderContext { - public Address Address { get; set; } + public string RouteName { get; set; } + + public RouteValueDictionary ExplicitValues { get; set; } public RouteValueDictionary AmbientValues { get; set; } - - public RouteValueDictionary SuppliedValues { get; set; } } } diff --git a/src/Microsoft.AspNetCore.Routing/Tree/OutboundRouteEntry.cs b/src/Microsoft.AspNetCore.Routing/Tree/OutboundRouteEntry.cs index 2364c3f350..9c5258df1a 100644 --- a/src/Microsoft.AspNetCore.Routing/Tree/OutboundRouteEntry.cs +++ b/src/Microsoft.AspNetCore.Routing/Tree/OutboundRouteEntry.cs @@ -58,5 +58,10 @@ namespace Microsoft.AspNetCore.Routing.Tree /// Gets or sets the . /// public RouteTemplate RouteTemplate { get; set; } + + /// + /// Gets or sets the data that is associated with this entry. + /// + public object Data { get; set; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Routing/Tree/TreeRouter.cs b/src/Microsoft.AspNetCore.Routing/Tree/TreeRouter.cs index a4735712f5..679cbf5261 100644 --- a/src/Microsoft.AspNetCore.Routing/Tree/TreeRouter.cs +++ b/src/Microsoft.AspNetCore.Routing/Tree/TreeRouter.cs @@ -150,7 +150,7 @@ namespace Microsoft.AspNetCore.Routing.Tree // The decision tree will give us back all entries that match the provided route data in the correct // order. We just need to iterate them and use the first one that can generate a link. - var matches = _linkGenerationTree.GetMatches(context); + var matches = _linkGenerationTree.GetMatches(context.Values, context.AmbientValues); if (matches == null) { diff --git a/test/Microsoft.AspNetCore.Routing.Tests/DefaultEndpointFinderTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/DefaultEndpointFinderTest.cs deleted file mode 100644 index a9421aad05..0000000000 --- a/test/Microsoft.AspNetCore.Routing.Tests/DefaultEndpointFinderTest.cs +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.AspNetCore.Routing.TestObjects; -using Microsoft.Extensions.Logging.Abstractions; -using Xunit; - -namespace Microsoft.AspNetCore.Routing -{ - public class DefaultEndpointFinderTest - { - [Fact] - public void FindEndpoints_IgnoresCase_ForRouteNameLookup() - { - // Arrange - var endpoint1 = CreateEndpoint(new Address("home")); - var endpoint2 = CreateEndpoint(new Address("admin")); - var endpointFinder = CreateDefaultEndpointFinder(endpoint1, endpoint2); - - // Act - var result = endpointFinder.FindEndpoints(new Address("Admin")); - - // Assert - var endpoint = Assert.Single(result); - Assert.Same(endpoint2, endpoint); - } - - [Fact] - public void FindEndpoints_MultipleEndpointsWithSameName_ReturnsFirstEndpoint_WithMatchingName() - { - // Arrange - var name = "common-tag-for-all-my-section's-routes"; - var endpoint1 = CreateEndpoint(new Address(name)); - var endpoint2 = CreateEndpoint(new Address("admin")); - var endpoint3 = CreateEndpoint(new Address(name)); - var endpoint4 = CreateEndpoint(new Address("products")); - var endpointFinder = CreateDefaultEndpointFinder(endpoint1, endpoint2, endpoint3, endpoint4); - - // Act - var result = endpointFinder.FindEndpoints(new Address(name)); - - // Assert - var endpoint = Assert.Single(result); - Assert.Same(endpoint, endpoint1); - } - - [Fact] - public void FindEndpoints_ReturnsAllEndpoints_WhenNoEndpointsHaveAddress() - { - // Arrange - var endpoint1 = CreateEndpoint(address: null); - var endpoint2 = CreateEndpoint(address: null); - var endpointFinder = CreateDefaultEndpointFinder(endpoint1, endpoint2); - - // Act - var result = endpointFinder.FindEndpoints(new Address("Admin")); - - // Assert - Assert.Collection( - result, - (ep) => Assert.Same(endpoint1, ep), - (ep) => Assert.Same(endpoint2, ep)); - } - - [Fact] - public void FindEndpoints_ReturnsAllEndpoints_WhenLookupAddress_IsNull() - { - // Arrange - var endpoint1 = CreateEndpoint(new Address("home")); - var endpoint2 = CreateEndpoint(new Address("admin")); - var endpointFinder = CreateDefaultEndpointFinder(endpoint1, endpoint2); - - // Act - var result = endpointFinder.FindEndpoints(lookupAddress: null); - - // Assert - Assert.Collection( - result, - (ep) => Assert.Same(endpoint1, ep), - (ep) => Assert.Same(endpoint2, ep)); - } - - [Fact] - public void FindEndpoints_ReturnsAllEndpoints_WhenNoEndpointsHaveAddress_AndLookupAddress_IsNull() - { - // Arrange - var endpoint1 = CreateEndpoint(address: null); - var endpoint2 = CreateEndpoint(address: null); - var endpointFinder = CreateDefaultEndpointFinder(endpoint1, endpoint2); - - // Act - var result = endpointFinder.FindEndpoints(lookupAddress: null); - - // Assert - Assert.Collection( - result, - (ep) => Assert.Same(endpoint1, ep), - (ep) => Assert.Same(endpoint2, ep)); - } - - [Fact] - public void FindEndpoints_ReturnsAllEndpoints_WhenNoInformationGiven_OnLookupAddress() - { - // Arrange - var endpoint1 = CreateEndpoint(new Address("home")); - var endpoint2 = CreateEndpoint(new Address("admin")); - var endpointFinder = CreateDefaultEndpointFinder(endpoint1, endpoint2); - - // Act - var result = endpointFinder.FindEndpoints(new Address(name: null)); - - // Assert - Assert.Collection( - result, - (ep) => Assert.Same(endpoint1, ep), - (ep) => Assert.Same(endpoint2, ep)); - } - - [Fact] - public void FindEndpoints_ReturnsEmpty_WhenNoEndpointFound_WithLookupAddress_Name() - { - // Arrange - var endpoint1 = CreateEndpoint(new Address("home")); - var endpoint2 = CreateEndpoint(new Address("admin")); - var endpointFinder = CreateDefaultEndpointFinder(endpoint1, endpoint2); - - // Act - var result = endpointFinder.FindEndpoints(new Address("DoesNotExist")); - - // Assert - Assert.Empty(result); - } - - private Endpoint CreateEndpoint(Address address) - { - return new TestEndpoint( - EndpointMetadataCollection.Empty, - displayName: null, - address: address); - } - - private DefaultEndpointFinder CreateDefaultEndpointFinder(params Endpoint[] endpoints) - { - return new DefaultEndpointFinder( - new CompositeEndpointDataSource(new[] { new DefaultEndpointDataSource(endpoints) }), - NullLogger.Instance); - } - - private class HomeController - { - public void Index() { } - public void Contact() { } - } - - private class AdminController - { - public void Index() { } - public void Contact() { } - } - } -} diff --git a/test/Microsoft.AspNetCore.Routing.Tests/DefaultLinkGeneratorTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/DefaultLinkGeneratorTest.cs index 892c69519b..ffb78a6ea1 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/DefaultLinkGeneratorTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/DefaultLinkGeneratorTest.cs @@ -4,10 +4,10 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.AspNetCore.Routing.EndpointFinders; using Microsoft.AspNetCore.Routing.Internal; using Microsoft.AspNetCore.Routing.Matchers; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.ObjectPool; using Moq; using Xunit; @@ -21,11 +21,11 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = CreateEndpoint("{controller}"); - var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); - var context = CreateLinkGeneratorContext(new { controller = "Home" }); + var linkGenerator = CreateLinkGenerator(); + var context = CreateRouteValuesContext(new { controller = "Home" }); // Act - var link = linkGenerator.GetLink(context); + var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // Assert Assert.Equal("/Home", link); @@ -37,11 +37,12 @@ namespace Microsoft.AspNetCore.Routing // Arrange var expectedMessage = "Could not find a matching endpoint to generate a link."; var endpoint = CreateEndpoint("{controller}/{action}"); - var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); - var context = CreateLinkGeneratorContext(new { controller = "Home" }); + var linkGenerator = CreateLinkGenerator(); + var context = CreateRouteValuesContext(new { controller = "Home" }); // Act & Assert - var exception = Assert.Throws(() => linkGenerator.GetLink(context)); + var exception = Assert.Throws( + () => linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues)); Assert.Equal(expectedMessage, exception.Message); } @@ -50,11 +51,15 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = CreateEndpoint("{controller}/{action}"); - var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); - var context = CreateLinkGeneratorContext(new { controller = "Home" }); + var linkGenerator = CreateLinkGenerator(); + var context = CreateRouteValuesContext(new { controller = "Home" }); // Act - var canGenerateLink = linkGenerator.TryGetLink(context, out var link); + var canGenerateLink = linkGenerator.TryGetLink( + new[] { endpoint }, + context.ExplicitValues, + context.AmbientValues, + out var link); // Assert Assert.False(canGenerateLink); @@ -68,11 +73,14 @@ namespace Microsoft.AspNetCore.Routing var endpoint1 = CreateEndpoint("{controller}/{action}/{id?}"); var endpoint2 = CreateEndpoint("{controller}/{action}"); var endpoint3 = CreateEndpoint("{controller}"); - var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint1, endpoint2, endpoint3)); - var context = CreateLinkGeneratorContext(new { controller = "Home", action = "Index", id = "10" }); + var linkGenerator = CreateLinkGenerator(); + var context = CreateRouteValuesContext(new { controller = "Home", action = "Index", id = "10" }); // Act - var link = linkGenerator.GetLink(context); + var link = linkGenerator.GetLink( + new[] { endpoint1, endpoint2, endpoint3 }, + context.ExplicitValues, + context.AmbientValues); // Assert Assert.Equal("/Home/Index/10", link); @@ -85,11 +93,14 @@ namespace Microsoft.AspNetCore.Routing var endpoint1 = CreateEndpoint("{controller}/{action}/{id}"); var endpoint2 = CreateEndpoint("{controller}/{action}"); var endpoint3 = CreateEndpoint("{controller}"); - var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint1, endpoint2, endpoint3)); - var context = CreateLinkGeneratorContext(new { controller = "Home", action = "Index" }); + var linkGenerator = CreateLinkGenerator(); + var context = CreateRouteValuesContext(new { controller = "Home", action = "Index" }); // Act - var link = linkGenerator.GetLink(context); + var link = linkGenerator.GetLink( + new[] { endpoint1, endpoint2, endpoint3 }, + context.ExplicitValues, + context.AmbientValues); // Assert Assert.Equal("/Home/Index", link); @@ -100,13 +111,13 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = CreateEndpoint("{controller}/{action}"); - var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); - var context = CreateLinkGeneratorContext( + var linkGenerator = CreateLinkGenerator(); + var context = CreateRouteValuesContext( suppliedValues: new { name = "name with %special #characters" }, ambientValues: new { controller = "Home", action = "Index" }); // Act - var link = linkGenerator.GetLink(context); + var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // Assert Assert.Equal("/Home/Index?name=name%20with%20%25special%20%23characters", link); @@ -117,13 +128,13 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = CreateEndpoint("{controller}/{action}"); - var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); - var context = CreateLinkGeneratorContext( + var linkGenerator = CreateLinkGenerator(); + var context = CreateRouteValuesContext( new { color = new List { "red", "green", "blue" } }, new { controller = "Home", action = "Index" }); // Act - var link = linkGenerator.GetLink(context); + var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // Assert Assert.Equal("/Home/Index?color=red&color=green&color=blue", link); @@ -134,13 +145,13 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = CreateEndpoint("{controller}/{action}"); - var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); - var context = CreateLinkGeneratorContext( + var linkGenerator = CreateLinkGenerator(); + var context = CreateRouteValuesContext( new { items = new List { 10, 20, 30 } }, new { controller = "Home", action = "Index" }); // Act - var link = linkGenerator.GetLink(context); + var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // Assert Assert.Equal("/Home/Index?items=10&items=20&items=30", link); @@ -151,13 +162,13 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = CreateEndpoint("{controller}/{action}"); - var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); - var context = CreateLinkGeneratorContext( + var linkGenerator = CreateLinkGenerator(); + var context = CreateRouteValuesContext( new { color = new List { } }, new { controller = "Home", action = "Index" }); // Act - var link = linkGenerator.GetLink(context); + var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // Assert Assert.Equal("/Home/Index", link); @@ -168,13 +179,13 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = CreateEndpoint("{controller}/{action}"); - var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); - var context = CreateLinkGeneratorContext( + var linkGenerator = CreateLinkGenerator(); + var context = CreateRouteValuesContext( new { page = 1, color = new List { "red", "green", "blue" }, message = "textfortest" }, new { controller = "Home", action = "Index" }); // Act - var link = linkGenerator.GetLink(context); + var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // Assert Assert.Equal("/Home/Index?page=1&color=red&color=green&color=blue&message=textfortest", link); @@ -185,13 +196,13 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = CreateEndpoint("{controller}/{action}"); - var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); - var context = CreateLinkGeneratorContext( + var linkGenerator = CreateLinkGenerator(); + var context = CreateRouteValuesContext( suppliedValues: new { action = "Index" }, ambientValues: new { controller = "Home" }); // Act - var link = linkGenerator.GetLink(context); + var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // Assert Assert.Equal("/Home/Index", link); @@ -201,9 +212,9 @@ namespace Microsoft.AspNetCore.Routing //public void RouteGenerationRejectsConstraints() //{ // // Arrange - // var context = CreateLinkGeneratorContext(new { p1 = "abcd" }); + // var context = CreateRouteValuesContext(new { p1 = "abcd" }); - // var endpoint = CreateEndpoint( + // var endpoint = CreateEndpoints( // "{p1}/{p2}", // new { p2 = "catchall" }, // true, @@ -220,16 +231,16 @@ namespace Microsoft.AspNetCore.Routing //public void RouteGenerationAcceptsConstraints() //{ // // Arrange - // var context = CreateLinkGeneratorContext(new { p1 = "hello", p2 = "1234" }); + // var context = CreateRouteValuesContext(new { p1 = "hello", p2 = "1234" }); - // var endpoint = CreateEndpoint( + // var endpoint = CreateEndpoints( // "{p1}/{p2}", // new { p2 = "catchall" }, // true, // new RouteValueDictionary(new { p2 = "\\d{4}" })); // // Act - // var link = linkGenerator.GetLink(context); + // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // // Assert // Assert.NotNull(pathData); @@ -242,9 +253,9 @@ namespace Microsoft.AspNetCore.Routing //public void RouteWithCatchAllRejectsConstraints() //{ // // Arrange - // var context = CreateLinkGeneratorContext(new { p1 = "abcd" }); + // var context = CreateRouteValuesContext(new { p1 = "abcd" }); - // var endpoint = CreateEndpoint( + // var endpoint = CreateEndpoints( // "{p1}/{*p2}", // new { p2 = "catchall" }, // true, @@ -261,16 +272,16 @@ namespace Microsoft.AspNetCore.Routing //public void RouteWithCatchAllAcceptsConstraints() //{ // // Arrange - // var context = CreateLinkGeneratorContext(new { p1 = "hello", p2 = "1234" }); + // var context = CreateRouteValuesContext(new { p1 = "hello", p2 = "1234" }); - // var endpoint = CreateEndpoint( + // var endpoint = CreateEndpoints( // "{p1}/{*p2}", // new { p2 = "catchall" }, // true, // new RouteValueDictionary(new { p2 = "\\d{4}" })); // // Act - // var link = linkGenerator.GetLink(context); + // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // // Assert // Assert.NotNull(pathData); @@ -283,7 +294,7 @@ namespace Microsoft.AspNetCore.Routing //public void GetLinkWithNonParameterConstraintReturnsUrlWithoutQueryString() //{ // // Arrange - // var context = CreateLinkGeneratorContext(new { p1 = "hello", p2 = "1234" }); + // var context = CreateRouteValuesContext(new { p1 = "hello", p2 = "1234" }); // var target = new Mock(); // target @@ -297,14 +308,14 @@ namespace Microsoft.AspNetCore.Routing // .Returns(true) // .Verifiable(); - // var endpoint = CreateEndpoint( + // var endpoint = CreateEndpoints( // "{p1}/{p2}", // new { p2 = "catchall" }, // true, // new RouteValueDictionary(new { p2 = target.Object })); // // Act - // var link = linkGenerator.GetLink(context); + // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // // Assert // Assert.NotNull(pathData); @@ -322,13 +333,13 @@ namespace Microsoft.AspNetCore.Routing //{ // // Arrange // var constraint = new CapturingConstraint(); - // var endpoint = CreateEndpoint( + // var endpoint = CreateEndpoints( // template: "slug/{controller}/{action}", // defaultValues: null, // handleRequest: true, // constraints: new { c = constraint }); - // var context = CreateLinkGeneratorContext( + // var context = CreateRouteValuesContext( // values: new { action = "Store" }, // ambientValues: new { Controller = "Home", action = "Blog", extra = "42" }); @@ -336,7 +347,7 @@ namespace Microsoft.AspNetCore.Routing // new { controller = "Home", action = "Store", extra = "42" }); // // Act - // var link = linkGenerator.GetLink(context); + // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // // Assert // Assert.Equal("/slug/Home/Store", link); @@ -353,13 +364,13 @@ namespace Microsoft.AspNetCore.Routing //{ // // Arrange // var constraint = new CapturingConstraint(); - // var endpoint = CreateEndpoint( + // var endpoint = CreateEndpoints( // template: "slug/{controller}/{action}", // defaultValues: new { otherthing = "17" }, // handleRequest: true, // constraints: new { c = constraint }); - // var context = CreateLinkGeneratorContext( + // var context = CreateRouteValuesContext( // values: new { action = "Store" }, // ambientValues: new { Controller = "Home", action = "Blog" }); @@ -367,7 +378,7 @@ namespace Microsoft.AspNetCore.Routing // new { controller = "Home", action = "Store" }); // // Act - // var link = linkGenerator.GetLink(context); + // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // // Assert // Assert.Equal("/slug/Home/Store", link); @@ -383,13 +394,13 @@ namespace Microsoft.AspNetCore.Routing //{ // // Arrange // var constraint = new CapturingConstraint(); - // var endpoint = CreateEndpoint( + // var endpoint = CreateEndpoints( // template: "slug/{controller}/{action}", // defaultValues: new { action = "Index" }, // handleRequest: true, // constraints: new { c = constraint }); - // var context = CreateLinkGeneratorContext( + // var context = CreateRouteValuesContext( // values: new { controller = "Shopping" }, // ambientValues: new { Controller = "Home", action = "Blog" }); @@ -397,7 +408,7 @@ namespace Microsoft.AspNetCore.Routing // new { controller = "Shopping", action = "Index" }); // // Act - // var link = linkGenerator.GetLink(context); + // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // // Assert // Assert.Equal("/slug/Shopping", link); @@ -414,13 +425,13 @@ namespace Microsoft.AspNetCore.Routing //{ // // Arrange // var constraint = new CapturingConstraint(); - // var endpoint = CreateEndpoint( + // var endpoint = CreateEndpoints( // template: "slug/{controller}/{action}", // defaultValues: new { otherthing = "17", thirdthing = "13" }, // handleRequest: true, // constraints: new { c = constraint }); - // var context = CreateLinkGeneratorContext( + // var context = CreateRouteValuesContext( // values: new { action = "Store", thirdthing = "13" }, // ambientValues: new { Controller = "Home", action = "Blog", otherthing = "17" }); @@ -428,7 +439,7 @@ namespace Microsoft.AspNetCore.Routing // new { controller = "Home", action = "Store", otherthing = "17", thirdthing = "13" }); // // Act - // var link = linkGenerator.GetLink(context); + // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // // Assert // Assert.Equal("/slug/Home/Store", link); @@ -442,12 +453,12 @@ namespace Microsoft.AspNetCore.Routing //public void GetLink_InlineConstraints_Success() //{ // // Arrange - // var endpoint = CreateEndpoint("{controller}/{action}/{id:int}"); - // var context = CreateLinkGeneratorContext( + // var endpoint = CreateEndpoints("{controller}/{action}/{id:int}"); + // var context = CreateRouteValuesContext( // values: new { action = "Index", controller = "Home", id = 4 }); // // Act - // var link = linkGenerator.GetLink(context); + // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // // Assert // Assert.Equal("/Home/Index/4", link); @@ -459,8 +470,8 @@ namespace Microsoft.AspNetCore.Routing //public void GetLink_InlineConstraints_NonMatchingvalue() //{ // // Arrange - // var endpoint = CreateEndpoint("{controller}/{action}/{id:int}"); - // var context = CreateLinkGeneratorContext( + // var endpoint = CreateEndpoints("{controller}/{action}/{id:int}"); + // var context = CreateRouteValuesContext( // values: new { action = "Index", controller = "Home", id = "asf" }); // // Act @@ -474,12 +485,12 @@ namespace Microsoft.AspNetCore.Routing //public void GetLink_InlineConstraints_OptionalParameter_ValuePresent() //{ // // Arrange - // var endpoint = CreateEndpoint("{controller}/{action}/{id:int?}"); - // var context = CreateLinkGeneratorContext( + // var endpoint = CreateEndpoints("{controller}/{action}/{id:int?}"); + // var context = CreateRouteValuesContext( // values: new { action = "Index", controller = "Home", id = 98 }); // // Act - // var link = linkGenerator.GetLink(context); + // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // // Assert // Assert.Equal("/Home/Index/98", link); @@ -491,12 +502,12 @@ namespace Microsoft.AspNetCore.Routing //public void GetLink_InlineConstraints_OptionalParameter_ValueNotPresent() //{ // // Arrange - // var endpoint = CreateEndpoint("{controller}/{action}/{id:int?}"); - // var context = CreateLinkGeneratorContext( + // var endpoint = CreateEndpoints("{controller}/{action}/{id:int?}"); + // var context = CreateRouteValuesContext( // values: new { action = "Index", controller = "Home" }); // // Act - // var link = linkGenerator.GetLink(context); + // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // // Assert // Assert.Equal("/Home/Index", link); @@ -508,8 +519,8 @@ namespace Microsoft.AspNetCore.Routing //public void GetLink_InlineConstraints_OptionalParameter_ValuePresent_ConstraintFails() //{ // // Arrange - // var endpoint = CreateEndpoint("{controller}/{action}/{id:int?}"); - // var context = CreateLinkGeneratorContext( + // var endpoint = CreateEndpoints("{controller}/{action}/{id:int?}"); + // var context = CreateRouteValuesContext( // values: new { action = "Index", controller = "Home", id = "sdfd" }); // // Act @@ -523,12 +534,12 @@ namespace Microsoft.AspNetCore.Routing //public void GetLink_InlineConstraints_CompositeInlineConstraint() //{ // // Arrange - // var endpoint = CreateEndpoint("{controller}/{action}/{id:int:range(1,20)}"); - // var context = CreateLinkGeneratorContext( + // var endpoint = CreateEndpoints("{controller}/{action}/{id:int:range(1,20)}"); + // var context = CreateRouteValuesContext( // values: new { action = "Index", controller = "Home", id = 14 }); // // Act - // var link = linkGenerator.GetLink(context); + // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // // Assert // Assert.Equal("/Home/Index/14", link); @@ -541,17 +552,17 @@ namespace Microsoft.AspNetCore.Routing //{ // // Arrange // var constraint = new MaxLengthRouteConstraint(20); - // var endpoint = CreateEndpoint( + // var endpoint = CreateEndpoints( // template: "{controller}/{action}/{name:alpha}", // defaultValues: null, // handleRequest: true, // constraints: new { name = constraint }); - // var context = CreateLinkGeneratorContext( + // var context = CreateRouteValuesContext( // values: new { action = "Index", controller = "Home", name = "products" }); // // Act - // var link = linkGenerator.GetLink(context); + // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // // Assert // Assert.Equal("/Home/Index/products", link); @@ -564,12 +575,12 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = CreateEndpoint("{controller}/{action}/{name?}"); - var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); - var context = CreateLinkGeneratorContext( + var linkGenerator = CreateLinkGenerator(); + var context = CreateRouteValuesContext( suppliedValues: new { action = "Index", controller = "Home", name = "products" }); // Act - var link = linkGenerator.GetLink(context); + var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // Assert Assert.Equal("/Home/Index/products", link); @@ -580,12 +591,12 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = CreateEndpoint("{controller}/{action}/{name?}"); - var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); - var context = CreateLinkGeneratorContext( + var linkGenerator = CreateLinkGenerator(); + var context = CreateRouteValuesContext( suppliedValues: new { action = "Index", controller = "Home" }); // Act - var link = linkGenerator.GetLink(context); + var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // Assert Assert.Equal("/Home/Index", link); @@ -598,12 +609,12 @@ namespace Microsoft.AspNetCore.Routing var endpoint = CreateEndpoint( template: "{controller}/{action}/{name?}", defaultValues: new { name = "default-products" }); - var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); - var context = CreateLinkGeneratorContext( + var linkGenerator = CreateLinkGenerator(); + var context = CreateRouteValuesContext( suppliedValues: new { action = "Index", controller = "Home", name = "products" }); // Act - var link = linkGenerator.GetLink(context); + var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // Assert Assert.Equal("/Home/Index/products", link); @@ -616,12 +627,12 @@ namespace Microsoft.AspNetCore.Routing var endpoint = CreateEndpoint( template: "{controller}/{action}/{name?}", defaultValues: new { name = "products" }); - var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); - var context = CreateLinkGeneratorContext( + var linkGenerator = CreateLinkGenerator(); + var context = CreateRouteValuesContext( suppliedValues: new { action = "Index", controller = "Home" }); // Act - var link = linkGenerator.GetLink(context); + var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // Assert Assert.Equal("/Home/Index", link); @@ -632,12 +643,12 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = CreateEndpoint("{controller}/{action}/{name}"); - var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); - var context = CreateLinkGeneratorContext( + var linkGenerator = CreateLinkGenerator(); + var context = CreateRouteValuesContext( suppliedValues: new { action = "Index", controller = "Home", name = "products", format = "json" }); // Act - var link = linkGenerator.GetLink(context); + var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // Assert Assert.Equal("/Home/Index/products?format=json", link); @@ -647,17 +658,17 @@ namespace Microsoft.AspNetCore.Routing //public void GetLink_OptionalParameter_FollowedByDotAfterSlash_ParameterPresent() //{ // // Arrange - // var endpoint = CreateEndpoint( + // var endpoint = CreateEndpoints( // template: "{controller}/{action}/.{name?}", // defaultValues: null, // handleRequest: true, // constraints: null); - // var context = CreateLinkGeneratorContext( + // var context = CreateRouteValuesContext( // values: new { action = "Index", controller = "Home", name = "products" }); // // Act - // var link = linkGenerator.GetLink(context); + // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // // Assert // Assert.Equal("/Home/Index/.products", link); @@ -669,17 +680,17 @@ namespace Microsoft.AspNetCore.Routing //public void GetLink_OptionalParameter_FollowedByDotAfterSlash_ParameterNotPresent() //{ // // Arrange - // var endpoint = CreateEndpoint( + // var endpoint = CreateEndpoints( // template: "{controller}/{action}/.{name?}", // defaultValues: null, // handleRequest: true, // constraints: null); - // var context = CreateLinkGeneratorContext( + // var context = CreateRouteValuesContext( // values: new { action = "Index", controller = "Home" }); // // Act - // var link = linkGenerator.GetLink(context); + // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // // Assert // Assert.Equal("/Home/Index/", link); @@ -692,12 +703,12 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = CreateEndpoint("{controller}/{action}/{name?}"); - var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); - var context = CreateLinkGeneratorContext( + var linkGenerator = CreateLinkGenerator(); + var context = CreateRouteValuesContext( suppliedValues: new { action = "Index", controller = "Home" }); // Act - var link = linkGenerator.GetLink(context); + var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // Assert Assert.Equal("/Home/Index", link); @@ -707,14 +718,14 @@ namespace Microsoft.AspNetCore.Routing //public void GetLink_TwoOptionalParameters_OneValueFromAmbientValues() //{ // // Arrange - // var endpoint = CreateEndpoint("a/{b=15}/{c?}/{d?}"); - // var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); - // var context = CreateLinkGeneratorContext( + // var endpoint = CreateEndpoints("a/{b=15}/{c?}/{d?}"); + // var linkGenerator = CreateLinkGenerator(); + // var context = CreateRouteValuesContext( // suppliedValues: new { }, // ambientValues: new { c = "17" }); // // Act - // var link = linkGenerator.GetLink(context); + // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // // Assert // Assert.Equal("/a/15/17", link); @@ -724,14 +735,14 @@ namespace Microsoft.AspNetCore.Routing //public void GetLink_OptionalParameterAfterDefault_OneValueFromAmbientValues() //{ // // Arrange - // var endpoint = CreateEndpoint("a/{b=15}/{c?}"); - // var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); - // var context = CreateLinkGeneratorContext( + // var endpoint = CreateEndpoints("a/{b=15}/{c?}"); + // var linkGenerator = CreateLinkGenerator(); + // var context = CreateRouteValuesContext( // suppliedValues: new { }, // ambientValues: new { c = "17" }); // // Act - // var link = linkGenerator.GetLink(context); + // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // // Assert // Assert.Equal("/a/15/17", link); @@ -741,18 +752,18 @@ namespace Microsoft.AspNetCore.Routing //public void GetLink_TwoOptionalParametersAfterDefault_LastValueFromAmbientValues() //{ // // Arrange - // var endpoint = CreateEndpoint( + // var endpoint = CreateEndpoints( // template: "a/{b=15}/{c?}/{d?}", // defaultValues: null, // handleRequest: true, // constraints: null); - // var context = CreateLinkGeneratorContext( + // var context = CreateRouteValuesContext( // values: new { }, // ambientValues: new { d = "17" }); // // Act - // var link = linkGenerator.GetLink(context); + // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues); // // Assert // Assert.NotNull(pathData); @@ -761,39 +772,32 @@ namespace Microsoft.AspNetCore.Routing // //} - private LinkGeneratorContext CreateLinkGeneratorContext(object suppliedValues, object ambientValues = null) + private RouteValuesBasedEndpointFinderContext CreateRouteValuesContext(object suppliedValues, object ambientValues = null) { - var context = new LinkGeneratorContext(); - context.SuppliedValues = new RouteValueDictionary(suppliedValues); + var context = new RouteValuesBasedEndpointFinderContext(); + context.ExplicitValues = new RouteValueDictionary(suppliedValues); context.AmbientValues = new RouteValueDictionary(ambientValues); return context; } private MatcherEndpoint CreateEndpoint(string template, object defaultValues = null) { + var defaults = defaultValues == null ? new RouteValueDictionary() : new RouteValueDictionary(defaultValues); return new MatcherEndpoint( next => (httpContext) => Task.CompletedTask, template, - defaultValues, + defaults, + new RouteValueDictionary(), 0, EndpointMetadataCollection.Empty, - null, - new Address("foo")); + null); } - private ILinkGenerator CreateLinkGenerator(IEndpointFinder endpointFinder) + private ILinkGenerator CreateLinkGenerator() { return new DefaultLinkGenerator( - endpointFinder, new DefaultObjectPool(new UriBuilderContextPooledObjectPolicy()), Mock.Of>()); } - - private DefaultEndpointFinder CreateEndpointFinder(params Endpoint[] endpoints) - { - return new DefaultEndpointFinder( - new CompositeEndpointDataSource(new[] { new DefaultEndpointDataSource(endpoints) }), - NullLogger.Instance); - } } } diff --git a/test/Microsoft.AspNetCore.Routing.Tests/EndpointConstraints/EndpointSelectorTests.cs b/test/Microsoft.AspNetCore.Routing.Tests/EndpointConstraints/EndpointSelectorTests.cs index 6438020dc8..796909c728 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/EndpointConstraints/EndpointSelectorTests.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/EndpointConstraints/EndpointSelectorTests.cs @@ -24,13 +24,11 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints // Arrange var defaultEndpoint = new TestEndpoint( EndpointMetadataCollection.Empty, - "No constraint endpoint", - address: null); + "No constraint endpoint"); var postEndpoint = new TestEndpoint( new EndpointMetadataCollection(new object[] { new HttpMethodEndpointConstraint(new[] { "POST" }) }), - "POST constraint endpoint", - address: null); + "POST constraint endpoint"); var endpoints = new Endpoint[] { @@ -62,13 +60,11 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints var defaultEndpoint1 = new TestEndpoint( EndpointMetadataCollection.Empty, - "Ambiguous1", - address: null); + "Ambiguous1"); var defaultEndpoint2 = new TestEndpoint( EndpointMetadataCollection.Empty, - "Ambiguous2", - address: null); + "Ambiguous2"); var endpoints = new Endpoint[] { @@ -100,8 +96,8 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints var actions = new Endpoint[] { - new TestEndpoint(EndpointMetadataCollection.Empty, "A1", address: null), - new TestEndpoint(EndpointMetadataCollection.Empty, "A2", address: null), + new TestEndpoint(EndpointMetadataCollection.Empty, "A1"), + new TestEndpoint(EndpointMetadataCollection.Empty, "A2"), }; var selector = CreateSelector(actions, loggerFactory); @@ -124,13 +120,11 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints // Arrange var actionWithConstraints = new TestEndpoint( new EndpointMetadataCollection(new[] { new HttpMethodEndpointConstraint(new string[] { "POST" }) }), - "Has constraint", - address: null); + "Has constraint"); var actionWithoutConstraints = new TestEndpoint( EndpointMetadataCollection.Empty, - "No constraint", - address: null); + "No constraint"); var actions = new Endpoint[] { actionWithConstraints, actionWithoutConstraints }; @@ -150,13 +144,11 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints // Arrange var action1 = new TestEndpoint( new EndpointMetadataCollection(new[] { new BooleanConstraint() { Pass = false, } }), - "action1", - address: null); + "action1"); var action2 = new TestEndpoint( new EndpointMetadataCollection(new[] { new BooleanConstraint() { Pass = false, } }), - "action2", - address: null); + "action2"); var actions = new Endpoint[] { action1, action2 }; @@ -179,16 +171,14 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints new BooleanConstraint() { Pass = false, Order = 0 }, new BooleanConstraint() { Pass = true, Order = 1 }, }), - "action1", - address: null); + "action1"); var action2 = new TestEndpoint(new EndpointMetadataCollection(new[] { new BooleanConstraint() { Pass = true, Order = 0 }, new BooleanConstraint() { Pass = false, Order = 1 }, }), - "action2", - address: null); + "action2"); var actions = new Endpoint[] { action1, action2 }; @@ -213,13 +203,11 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints Constraint = new BooleanConstraint() { Pass = true }, }, }), - "actionWithConstraints", - address: null); + "actionWithConstraints"); var actionWithoutConstraints = new TestEndpoint( EndpointMetadataCollection.Empty, - "actionWithoutConstraints", - address: null); + "actionWithoutConstraints"); var actions = new Endpoint[] { actionWithConstraints, actionWithoutConstraints }; @@ -237,7 +225,7 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints public void SelectBestCandidate_MultipleCallsNoConstraint_ReturnsEndpoint() { // Arrange - var noConstraint = new TestEndpoint(EndpointMetadataCollection.Empty, "noConstraint", address: null); + var noConstraint = new TestEndpoint(EndpointMetadataCollection.Empty, "noConstraint"); var actions = new Endpoint[] { noConstraint }; @@ -261,8 +249,7 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints { new object(), }), - "noConstraint", - address: null); + "noConstraint"); var actions = new Endpoint[] { noConstraint }; @@ -286,8 +273,7 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints { new ConstraintFactory(), }), - "nullConstraint", - address: null); + "nullConstraint"); var actions = new Endpoint[] { nullConstraint }; @@ -312,13 +298,11 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints { new BooleanConstraintMarker() { Pass = true }, }), - "actionWithConstraints", - address: null); + "actionWithConstraints"); var actionWithoutConstraints = new TestEndpoint( EndpointMetadataCollection.Empty, - "actionWithoutConstraints", - address: null); + "actionWithoutConstraints"); var actions = new Endpoint[] { actionWithConstraints, actionWithoutConstraints, }; @@ -341,15 +325,13 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints { new BooleanConstraint() { Pass = true, Order = 0, }, }), - "best", - address: null); + "best"); var worst = new TestEndpoint(new EndpointMetadataCollection(new[] { new BooleanConstraint() { Pass = true, Order = 1, }, }), - "worst", - address: null); + "worst"); var actions = new Endpoint[] { best, worst }; @@ -374,8 +356,7 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints new BooleanConstraint() { Pass = true, Order = 1, }, new BooleanConstraint() { Pass = true, Order = 2, }, }), - "best", - address: null); + "best"); var worst = new TestEndpoint(new EndpointMetadataCollection(new[] { @@ -383,8 +364,7 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints new BooleanConstraint() { Pass = true, Order = 1, }, new BooleanConstraint() { Pass = true, Order = 3, }, }), - "worst", - address: null); + "worst"); var actions = new Endpoint[] { best, worst }; @@ -408,8 +388,7 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints new BooleanConstraint() { Pass = true, Order = 1, }, new BooleanConstraint() { Pass = false, Order = 2, }, }), - "nomatch1", - address: null); + "nomatch1"); var nomatch2 = new TestEndpoint(new EndpointMetadataCollection(new[] { @@ -417,10 +396,9 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints new BooleanConstraint() { Pass = true, Order = 1, }, new BooleanConstraint() { Pass = false, Order = 3, }, }), - "nomatch2", - address: null); + "nomatch2"); - var best = new TestEndpoint(EndpointMetadataCollection.Empty, "best", address: null); + var best = new TestEndpoint(EndpointMetadataCollection.Empty, "best"); var actions = new Endpoint[] { best, nomatch1, nomatch2 }; diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Internal/HttpMethodEndpointConstraintTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/Internal/HttpMethodEndpointConstraintTest.cs index 1d79fc1b9b..67af016137 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Internal/HttpMethodEndpointConstraintTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Internal/HttpMethodEndpointConstraintTest.cs @@ -62,7 +62,7 @@ namespace Microsoft.AspNetCore.Routing.Internal var context = new EndpointConstraintContext(); var endpointSelectorCandidate = new EndpointSelectorCandidate( - new TestEndpoint(EndpointMetadataCollection.Empty, string.Empty, address: null), + new TestEndpoint(EndpointMetadataCollection.Empty, string.Empty), new List { constraint }); context.Candidates = new List { endpointSelectorCandidate }; diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Internal/LinkGenerationDecisionTreeTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/Internal/LinkGenerationDecisionTreeTest.cs index 8a35edcf13..c8c34b09d4 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Internal/LinkGenerationDecisionTreeTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Internal/LinkGenerationDecisionTreeTest.cs @@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing var context = CreateContext(new { }); // Act - var matches = tree.GetMatches(context); + var matches = tree.GetMatches(context.Values, context.AmbientValues); // Assert Assert.Same(entry, Assert.Single(matches).Match); @@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing var context = CreateContext(new { controller = "Store", action = "Buy" }); // Act - var matches = tree.GetMatches(context); + var matches = tree.GetMatches(context.Values, context.AmbientValues); // Assert Assert.Same(entry, Assert.Single(matches).Match); @@ -66,7 +66,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing var context = CreateContext(values: null, ambientValues: new { controller = "Store", action = "Buy" }); // Act - var matches = tree.GetMatches(context); + var matches = tree.GetMatches(context.Values, context.AmbientValues); // Assert var match = Assert.Single(matches); @@ -90,7 +90,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing ambientValues: new { controller = "Store", action = "Cart" }); // Act - var matches = tree.GetMatches(context); + var matches = tree.GetMatches(context.Values, context.AmbientValues); // Assert var match = Assert.Single(matches); @@ -114,7 +114,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing ambientValues: new { controller = "Store", action = "Buy" }); // Act - var matches = tree.GetMatches(context); + var matches = tree.GetMatches(context.Values, context.AmbientValues); // Assert var match = Assert.Single(matches); @@ -136,7 +136,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing var context = CreateContext(new { controller = "Store", action = "AddToCart" }); // Act - var matches = tree.GetMatches(context); + var matches = tree.GetMatches(context.Values, context.AmbientValues); // Assert Assert.Empty(matches); @@ -158,7 +158,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing ambientValues: new { controller = "Store", action = "Cart" }); // Act - var matches = tree.GetMatches(context); + var matches = tree.GetMatches(context.Values, context.AmbientValues); // Assert Assert.Empty(matches); @@ -183,7 +183,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing ambientValues: new { controller = "Store", action = "Buy" }); // Act - var matches = tree.GetMatches(context); + var matches = tree.GetMatches(context.Values, context.AmbientValues); // Assert Assert.Same(entry1, Assert.Single(matches).Match); @@ -209,7 +209,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing ambientValues: new { controller = "Store", action = "Buy" }); // Act - var matches = tree.GetMatches(context).Select(m => m.Match).ToList(); + var matches = tree.GetMatches(context.Values, context.AmbientValues).Select(m => m.Match).ToList(); // Assert Assert.Equal(entries, matches); @@ -233,7 +233,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing var context = CreateContext(new { controller = "Store", action = "Buy", slug = "1234" }); // Act - var matches = tree.GetMatches(context).Select(m => m.Match).ToList(); + var matches = tree.GetMatches(context.Values, context.AmbientValues).Select(m => m.Match).ToList(); // Assert Assert.Equal(entries, matches); @@ -260,7 +260,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing var context = CreateContext(new { controller = "Store", action = "Buy" }); // Act - var matches = tree.GetMatches(context).Select(m => m.Match).ToList(); + var matches = tree.GetMatches(context.Values, context.AmbientValues).Select(m => m.Match).ToList(); // Assert Assert.Equal(entries, matches); @@ -286,7 +286,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing var context = CreateContext(new { controller = "Store", action = "Buy" }); // Act - var matches = tree.GetMatches(context).Select(m => m.Match).ToList(); + var matches = tree.GetMatches(context.Values, context.AmbientValues).Select(m => m.Match).ToList(); // Assert Assert.Equal(entries, matches); @@ -312,7 +312,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing var context = CreateContext(new { controller = "Store", action = "Buy" }); // Act - var matches = tree.GetMatches(context).Select(m => m.Match).ToList(); + var matches = tree.GetMatches(context.Values, context.AmbientValues).Select(m => m.Match).ToList(); // Assert Assert.Equal(entries, matches); diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matchers/MatcherConformanceTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matchers/MatcherConformanceTest.cs index eb331a328a..bbb08d5f9a 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matchers/MatcherConformanceTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matchers/MatcherConformanceTest.cs @@ -34,17 +34,18 @@ namespace Microsoft.AspNetCore.Routing.Matchers internal static MatcherEndpoint CreateEndpoint( string template, - object defaults = null, + object defaultValues = null, int? order = null) { + var defaults = defaultValues == null ? new RouteValueDictionary() : new RouteValueDictionary(defaultValues); return new MatcherEndpoint( MatcherEndpoint.EmptyInvoker, template, defaults, + new RouteValueDictionary(), order ?? 0, EndpointMetadataCollection.Empty, - "endpoint: " + template, - address: null); + "endpoint: " + template); } internal (Matcher matcher, MatcherEndpoint endpoint) CreateMatcher(string template) diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matchers/RouteMatcherBuilder.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matchers/RouteMatcherBuilder.cs index e411d69647..7ce7613fe5 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matchers/RouteMatcherBuilder.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matchers/RouteMatcherBuilder.cs @@ -31,10 +31,10 @@ namespace Microsoft.AspNetCore.Routing.Matchers // MatcherEndpoint.Values contains the default values parsed from the template // as well as those specified with a literal. We need to separate those // for legacy cases. - var defaults = new RouteValueDictionary(endpoint.Values); - for (var i = 0; i < endpoint.ParsedTemlate.Parameters.Count; i++) + var defaults = endpoint.Defaults; + for (var i = 0; i < endpoint.ParsedTemplate.Parameters.Count; i++) { - var parameter = endpoint.ParsedTemlate.Parameters[i]; + var parameter = endpoint.ParsedTemplate.Parameters[i]; if (parameter.DefaultValue != null) { defaults.Remove(parameter.Name); @@ -79,7 +79,7 @@ namespace Microsoft.AspNetCore.Routing.Matchers return comparison; } - comparison = RoutePrecedence.ComputeInbound(Endpoint.ParsedTemlate).CompareTo(RoutePrecedence.ComputeInbound(other.Endpoint.ParsedTemlate)); + comparison = RoutePrecedence.ComputeInbound(Endpoint.ParsedTemplate).CompareTo(RoutePrecedence.ComputeInbound(other.Endpoint.ParsedTemplate)); if (comparison != 0) { return comparison; diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matchers/TreeMatcherTests.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matchers/TreeMatcherTests.cs index c21a90dfda..cc865fed75 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matchers/TreeMatcherTests.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matchers/TreeMatcherTests.cs @@ -1,23 +1,22 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Collections.Generic; +using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing.EndpointConstraints; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; using Xunit; namespace Microsoft.AspNetCore.Routing.Matchers { public class TreeMatcherTests { - private MatcherEndpoint CreateEndpoint(string template, int order, object values = null, EndpointMetadataCollection metadata = null) + private MatcherEndpoint CreateEndpoint(string template, int order, object defaultValues = null, EndpointMetadataCollection metadata = null) { - return new MatcherEndpoint((next) => null, template, values, order, metadata ?? EndpointMetadataCollection.Empty, template, address: null); + var defaults = defaultValues == null ? new RouteValueDictionary() : new RouteValueDictionary(defaultValues); + return new MatcherEndpoint((next) => null, template, defaults, new RouteValueDictionary(), order, metadata ?? EndpointMetadataCollection.Empty, template); } private TreeMatcher CreateTreeMatcher(EndpointDataSource endpointDataSource) diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matchers/TreeRouterMatcherBuilder.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matchers/TreeRouterMatcherBuilder.cs index 50f6e5ace3..c9f100439f 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matchers/TreeRouterMatcherBuilder.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matchers/TreeRouterMatcherBuilder.cs @@ -4,7 +4,6 @@ using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Routing.Internal; -using Microsoft.AspNetCore.Routing.Template; using Microsoft.AspNetCore.Routing.Tree; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.ObjectPool; @@ -38,10 +37,10 @@ namespace Microsoft.AspNetCore.Routing.Matchers // MatcherEndpoint.Values contains the default values parsed from the template // as well as those specified with a literal. We need to separate those // for legacy cases. - var defaults = new RouteValueDictionary(endpoint.Values); - for (var i = 0; i < endpoint.ParsedTemlate.Parameters.Count; i++) + var defaults = endpoint.Defaults; + for (var i = 0; i < endpoint.ParsedTemplate.Parameters.Count; i++) { - var parameter = endpoint.ParsedTemlate.Parameters[i]; + var parameter = endpoint.ParsedTemplate.Parameters[i]; if (parameter.DefaultValue == null && defaults.ContainsKey(parameter.Name)) { throw new InvalidOperationException( @@ -50,8 +49,8 @@ namespace Microsoft.AspNetCore.Routing.Matchers } _inner.MapInbound( - handler, - endpoint.ParsedTemlate, + handler, + endpoint.ParsedTemplate, routeName: null, order: endpoint.Order); } diff --git a/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestEndpoint.cs b/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestEndpoint.cs index 92a1ac8d1b..ab0ff046d8 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestEndpoint.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestEndpoint.cs @@ -5,8 +5,8 @@ namespace Microsoft.AspNetCore.Routing.TestObjects { internal class TestEndpoint : Endpoint { - public TestEndpoint(EndpointMetadataCollection metadata, string displayName, Address address) - : base(metadata, displayName, address) + public TestEndpoint(EndpointMetadataCollection metadata, string displayName) + : base(metadata, displayName) { } } diff --git a/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestMatcher.cs b/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestMatcher.cs index 5ee7031fb5..58b8f37840 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestMatcher.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestMatcher.cs @@ -1,8 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing.Matchers; @@ -22,7 +20,7 @@ namespace Microsoft.AspNetCore.Routing.TestObjects { if (_isHandled) { - feature.Endpoint = new TestEndpoint(EndpointMetadataCollection.Empty, "Test endpoint", address: null); + feature.Endpoint = new TestEndpoint(EndpointMetadataCollection.Empty, "Test endpoint"); } return Task.CompletedTask;