Added a RouteValuesBasedEndpointFinder

This commit is contained in:
Kiran Challa 2018-06-25 12:21:15 -07:00
parent bc5f02444b
commit 1009705283
30 changed files with 502 additions and 502 deletions

View File

@ -32,11 +32,11 @@ namespace Benchmarks
return response.Body.WriteAsync(_helloWorldPayload, 0, payloadLength); return response.Body.WriteAsync(_helloWorldPayload, 0, payloadLength);
}, },
template: "/plaintext", template: "/plaintext",
values: new { }, defaults: new RouteValueDictionary(),
requiredValues: new RouteValueDictionary(),
order: 0, order: 0,
metadata: EndpointMetadataCollection.Empty, metadata: EndpointMetadataCollection.Empty,
displayName: "Plaintext", displayName: "Plaintext"),
address: null),
})); }));
}); });
} }

View File

@ -28,11 +28,11 @@ namespace Microsoft.AspNetCore.Routing.Matchers
return new MatcherEndpoint( return new MatcherEndpoint(
(next) => (context) => Task.CompletedTask, (next) => (context) => Task.CompletedTask,
template, template,
new { }, new RouteValueDictionary(),
new RouteValueDictionary(),
0, 0,
EndpointMetadataCollection.Empty, EndpointMetadataCollection.Empty,
template, template);
address: null);
} }
internal static int[] SampleRequests(int endpointCount, int count) internal static int[] SampleRequests(int endpointCount, int count)

View File

@ -31,7 +31,7 @@ namespace DispatcherSample.Web
response.ContentLength = payloadLength; response.ContentLength = payloadLength;
return response.Body.WriteAsync(_homePayload, 0, 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) => new MatcherEndpoint((next) => (httpContext) =>
{ {
var response = httpContext.Response; var response = httpContext.Response;
@ -41,7 +41,7 @@ namespace DispatcherSample.Web
response.ContentLength = payloadLength; response.ContentLength = payloadLength;
return response.Body.WriteAsync(_helloWorldPayload, 0, 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"),
})); }));
}); });
} }

View File

@ -10,19 +10,15 @@ namespace Microsoft.AspNetCore.Routing
{ {
protected Endpoint( protected Endpoint(
EndpointMetadataCollection metadata, EndpointMetadataCollection metadata,
string displayName, string displayName)
Address address)
{ {
// All are allowed to be null // All are allowed to be null
Metadata = metadata ?? EndpointMetadataCollection.Empty; Metadata = metadata ?? EndpointMetadataCollection.Empty;
DisplayName = displayName; DisplayName = displayName;
Address = address;
} }
public string DisplayName { get; } public string DisplayName { get; }
public EndpointMetadataCollection Metadata { get; } public EndpointMetadataCollection Metadata { get; }
public Address Address { get; }
} }
} }

View File

@ -5,8 +5,8 @@ using System.Collections.Generic;
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing
{ {
public interface IEndpointFinder public interface IEndpointFinder<TAddress>
{ {
IEnumerable<Endpoint> FindEndpoints(Address address); IEnumerable<Endpoint> FindEndpoints(TAddress address);
} }
} }

View File

@ -1,12 +1,21 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 namespace Microsoft.AspNetCore.Routing
{ {
public interface ILinkGenerator public interface ILinkGenerator
{ {
bool TryGetLink(LinkGeneratorContext context, out string link); bool TryGetLink(
IEnumerable<Endpoint> endpoints,
RouteValueDictionary explicitValues,
RouteValueDictionary ambientValues,
out string link);
string GetLink(LinkGeneratorContext context); string GetLink(
IEnumerable<Endpoint> endpoints,
RouteValueDictionary explicitValues,
RouteValueDictionary ambientValues);
} }
} }

View File

@ -1,19 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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
{ {
public class Address public interface INameMetadata
{ {
public Address() string Name { get; }
{
}
public Address(string name)
{
Name = name;
}
public string Name { get; set; }
} }
} }

View File

@ -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; }
}
}

View File

@ -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<DefaultEndpointFinder> _logger;
public DefaultEndpointFinder(
CompositeEndpointDataSource endpointDataSource,
ILogger<DefaultEndpointFinder> logger)
{
_endpointDatasource = endpointDataSource;
_logger = logger;
}
public IEnumerable<Endpoint> 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<Endpoint>();
}
}
}

View File

@ -15,23 +15,23 @@ namespace Microsoft.AspNetCore.Routing
{ {
public class DefaultLinkGenerator : ILinkGenerator public class DefaultLinkGenerator : ILinkGenerator
{ {
private readonly IEndpointFinder _endpointFinder;
private readonly ObjectPool<UriBuildingContext> _uriBuildingContextPool; private readonly ObjectPool<UriBuildingContext> _uriBuildingContextPool;
private readonly ILogger<DefaultLinkGenerator> _logger; private readonly ILogger<DefaultLinkGenerator> _logger;
public DefaultLinkGenerator( public DefaultLinkGenerator(
IEndpointFinder endpointFinder,
ObjectPool<UriBuildingContext> uriBuildingContextPool, ObjectPool<UriBuildingContext> uriBuildingContextPool,
ILogger<DefaultLinkGenerator> logger) ILogger<DefaultLinkGenerator> logger)
{ {
_endpointFinder = endpointFinder;
_uriBuildingContextPool = uriBuildingContextPool; _uriBuildingContextPool = uriBuildingContextPool;
_logger = logger; _logger = logger;
} }
public string GetLink(LinkGeneratorContext context) public string GetLink(
IEnumerable<Endpoint> endpoints,
RouteValueDictionary explicitValues,
RouteValueDictionary ambientValues)
{ {
if (TryGetLink(context, out var link)) if (TryGetLink(endpoints, explicitValues, ambientValues, out var link))
{ {
return link; return link;
} }
@ -39,10 +39,12 @@ namespace Microsoft.AspNetCore.Routing
throw new InvalidOperationException("Could not find a matching endpoint to generate a link."); 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<Endpoint> endpoints,
RouteValueDictionary explicitValues,
RouteValueDictionary ambientValues,
out string link)
{ {
var address = context.Address;
var endpoints = _endpointFinder.FindEndpoints(address);
link = null; link = null;
if (endpoints == null) if (endpoints == null)
@ -59,7 +61,7 @@ namespace Microsoft.AspNetCore.Routing
foreach (var endpoint in matcherEndpoints) foreach (var endpoint in matcherEndpoints)
{ {
link = GetLink(endpoint.ParsedTemlate, endpoint.Values, context); link = GetLink(endpoint.ParsedTemplate, endpoint.Defaults, explicitValues, ambientValues);
if (link != null) if (link != null)
{ {
return true; return true;
@ -71,19 +73,17 @@ namespace Microsoft.AspNetCore.Routing
private string GetLink( private string GetLink(
RouteTemplate template, RouteTemplate template,
IReadOnlyDictionary<string, object> defaultValues, RouteValueDictionary defaults,
LinkGeneratorContext context) RouteValueDictionary explicitValues,
RouteValueDictionary ambientValues)
{ {
var defaults = new RouteValueDictionary(defaultValues);
var templateBinder = new TemplateBinder( var templateBinder = new TemplateBinder(
UrlEncoder.Default, UrlEncoder.Default,
_uriBuildingContextPool, _uriBuildingContextPool,
template, template,
defaults); defaults);
var values = templateBinder.GetValues( var values = templateBinder.GetValues(ambientValues, explicitValues);
new RouteValueDictionary(context.AmbientValues),
new RouteValueDictionary(context.SuppliedValues));
if (values == null) if (values == null)
{ {
// We're missing one of the required values for this route. // We're missing one of the required values for this route.

View File

@ -2,8 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Reflection;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.EndpointConstraints; using Microsoft.AspNetCore.Routing.EndpointConstraints;
using Microsoft.AspNetCore.Routing.EndpointFinders;
using Microsoft.AspNetCore.Routing.Matchers; using Microsoft.AspNetCore.Routing.Matchers;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -35,7 +37,8 @@ namespace Microsoft.Extensions.DependencyInjection
services.TryAddSingleton<MatcherFactory, TreeMatcherFactory>(); services.TryAddSingleton<MatcherFactory, TreeMatcherFactory>();
// Link generation related services // Link generation related services
services.TryAddSingleton<IEndpointFinder, DefaultEndpointFinder>(); services.TryAddSingleton<IEndpointFinder<string>, NameBasedEndpointFinder>();
services.TryAddSingleton<IEndpointFinder<RouteValuesBasedEndpointFinderContext>, RouteValuesBasedEndpointFinder>();
services.TryAddSingleton<ILinkGenerator, DefaultLinkGenerator>(); services.TryAddSingleton<ILinkGenerator, DefaultLinkGenerator>();
// //
// Endpoint Selection // Endpoint Selection

View File

@ -20,18 +20,18 @@ namespace Microsoft.AspNetCore.Routing.Internal
new OutboundMatchClassifier()); new OutboundMatchClassifier());
} }
public IList<OutboundMatchResult> GetMatches(VirtualPathContext context) public IList<OutboundMatchResult> GetMatches(RouteValueDictionary values, RouteValueDictionary ambientValues)
{ {
// Perf: Avoid allocation for List if there aren't any Matches or Criteria // Perf: Avoid allocation for List if there aren't any Matches or Criteria
if (_root.Matches.Count > 0 || _root.Criteria.Count > 0) if (_root.Matches.Count > 0 || _root.Criteria.Count > 0)
{ {
var results = new List<OutboundMatchResult>(); var results = new List<OutboundMatchResult>();
Walk(results, context, _root, isFallbackPath: false); Walk(results, values, ambientValues, _root, isFallbackPath: false);
results.Sort(OutboundMatchResultComparer.Instance); results.Sort(OutboundMatchResultComparer.Instance);
return results; return results;
} }
return null; return null;
} }
// We need to recursively walk the decision tree based on the provided route data // 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. // The decision tree uses a tree data structure to execute these rules across all candidates at once.
private void Walk( private void Walk(
List<OutboundMatchResult> results, List<OutboundMatchResult> results,
VirtualPathContext context, RouteValueDictionary values,
RouteValueDictionary ambientValues,
DecisionTreeNode<OutboundMatch> node, DecisionTreeNode<OutboundMatch> node,
bool isFallbackPath) bool isFallbackPath)
{ {
@ -78,12 +79,12 @@ namespace Microsoft.AspNetCore.Routing.Internal
var key = criterion.Key; var key = criterion.Key;
object value; object value;
if (context.Values.TryGetValue(key, out value)) if (values.TryGetValue(key, out value))
{ {
DecisionTreeNode<OutboundMatch> branch; DecisionTreeNode<OutboundMatch> branch;
if (criterion.Branches.TryGetValue(value ?? string.Empty, out branch)) if (criterion.Branches.TryGetValue(value ?? string.Empty, out branch))
{ {
Walk(results, context, branch, isFallbackPath); Walk(results, values, ambientValues, branch, isFallbackPath);
} }
} }
else 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 // if an ambient value was supplied. The path explored with the empty value is considered
// the fallback path. // the fallback path.
DecisionTreeNode<OutboundMatch> branch; DecisionTreeNode<OutboundMatch> branch;
if (context.AmbientValues.TryGetValue(key, out value) && if (ambientValues.TryGetValue(key, out value) &&
!criterion.Branches.Comparer.Equals(value, string.Empty)) !criterion.Branches.Comparer.Equals(value, string.Empty))
{ {
if (criterion.Branches.TryGetValue(value, out branch)) 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)) 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( return StringComparer.Ordinal.Compare(
x.Match.Entry.RouteTemplate.TemplateText, x.Match.Entry.RouteTemplate.TemplateText,
y.Match.Entry.RouteTemplate.TemplateText); y.Match.Entry.RouteTemplate.TemplateText);
} }
} }

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Template; using Microsoft.AspNetCore.Routing.Template;
@ -19,12 +18,12 @@ namespace Microsoft.AspNetCore.Routing.Matchers
public MatcherEndpoint( public MatcherEndpoint(
Func<RequestDelegate, RequestDelegate> invoker, Func<RequestDelegate, RequestDelegate> invoker,
string template, string template,
object values, RouteValueDictionary defaults,
RouteValueDictionary requiredValues,
int order, int order,
EndpointMetadataCollection metadata, EndpointMetadataCollection metadata,
string displayName, string displayName)
Address address) : base(metadata, displayName)
: base(metadata, displayName, address)
{ {
if (invoker == null) if (invoker == null)
{ {
@ -37,24 +36,31 @@ namespace Microsoft.AspNetCore.Routing.Matchers
} }
Invoker = invoker; Invoker = invoker;
Template = template;
ParsedTemlate = TemplateParser.Parse(template);
var mergedDefaults = GetDefaults(ParsedTemlate, new RouteValueDictionary(values));
Values = mergedDefaults;
Order = order; Order = order;
Template = template;
ParsedTemplate = TemplateParser.Parse(template);
RequiredValues = requiredValues;
var mergedDefaults = GetDefaults(ParsedTemplate, defaults);
Defaults = mergedDefaults;
} }
public int Order { get; } public int Order { get; }
public Func<RequestDelegate, RequestDelegate> Invoker { get; } public Func<RequestDelegate, RequestDelegate> Invoker { get; }
public string Template { get; } public string Template { get; }
public IReadOnlyDictionary<string, object> 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 // 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) foreach (var parameter in parsedTemplate.Parameters)
{ {

View File

@ -100,7 +100,7 @@ namespace Microsoft.AspNetCore.Routing.Matchers
{ {
if (feature.Endpoint is MatcherEndpoint endpoint) if (feature.Endpoint is MatcherEndpoint endpoint)
{ {
foreach (var kvp in endpoint.Values) foreach (var kvp in endpoint.Defaults)
{ {
if (!feature.Values.ContainsKey(kvp.Key)) if (!feature.Values.ContainsKey(kvp.Key))
{ {

View File

@ -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<string>
{
private readonly CompositeEndpointDataSource _endpointDatasource;
private readonly ILogger<NameBasedEndpointFinder> _logger;
public NameBasedEndpointFinder(
CompositeEndpointDataSource endpointDataSource,
ILogger<NameBasedEndpointFinder> logger)
{
_endpointDatasource = endpointDataSource;
_logger = logger;
}
public IEnumerable<Endpoint> FindEndpoints(string endpointName)
{
if (string.IsNullOrEmpty(endpointName))
{
return Array.Empty<Endpoint>();
}
var endpoints = _endpointDatasource.Endpoints.OfType<MatcherEndpoint>();
foreach (var endpoint in endpoints)
{
var nameMetadata = endpoint.Metadata.GetMetadata<INameMetadata>();
if (nameMetadata != null &&
string.Equals(endpointName, nameMetadata.Name, StringComparison.OrdinalIgnoreCase))
{
return new[] { endpoint };
}
}
return Array.Empty<Endpoint>();
}
}
}

View File

@ -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<RouteValuesBasedEndpointFinderContext>
{
private readonly CompositeEndpointDataSource _endpointDataSource;
private readonly IInlineConstraintResolver _inlineConstraintResolver;
private readonly ObjectPool<UriBuildingContext> _objectPool;
private LinkGenerationDecisionTree _allMatchesLinkGenerationTree;
private IDictionary<string, LinkGenerationDecisionTree> _namedMatches;
public RouteValuesBasedEndpointFinder(
CompositeEndpointDataSource endpointDataSource,
ObjectPool<UriBuildingContext> objectPool,
IInlineConstraintResolver inlineConstraintResolver)
{
_endpointDataSource = endpointDataSource;
_objectPool = objectPool;
_inlineConstraintResolver = inlineConstraintResolver;
BuildOutboundMatches();
}
public IEnumerable<Endpoint> FindEndpoints(RouteValuesBasedEndpointFinderContext context)
{
IEnumerable<OutboundMatchResult> 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<Endpoint>();
}
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<OutboundMatch>, IDictionary<string, List<OutboundMatch>>) GetOutboundMatches()
{
var allOutboundMatches = new List<OutboundMatch>();
var namedOutboundMatches = new Dictionary<string, List<OutboundMatch>>(StringComparer.OrdinalIgnoreCase);
var endpoints = _endpointDataSource.Endpoints.OfType<MatcherEndpoint>();
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<OutboundMatch> matches;
if (!namedOutboundMatches.TryGetValue(entry.RouteName, out matches))
{
matches = new List<OutboundMatch>();
namedOutboundMatches.Add(entry.RouteName, matches);
}
matches.Add(outboundMatch);
}
return (allOutboundMatches, namedOutboundMatches);
}
private OutboundRouteEntry CreateOutboundRouteEntry(MatcherEndpoint endpoint)
{
var routeNameMetadata = endpoint.Metadata.GetMetadata<IRouteNameMetadata>();
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<string, LinkGenerationDecisionTree> GetNamedMatches(
IDictionary<string, List<OutboundMatch>> namedOutboundMatches)
{
var result = new Dictionary<string, LinkGenerationDecisionTree>(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();
}
}
}
}

View File

@ -1,14 +1,14 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 AmbientValues { get; set; }
public RouteValueDictionary SuppliedValues { get; set; }
} }
} }

View File

@ -58,5 +58,10 @@ namespace Microsoft.AspNetCore.Routing.Tree
/// Gets or sets the <see cref="RouteTemplate"/>. /// Gets or sets the <see cref="RouteTemplate"/>.
/// </summary> /// </summary>
public RouteTemplate RouteTemplate { get; set; } public RouteTemplate RouteTemplate { get; set; }
/// <summary>
/// Gets or sets the data that is associated with this entry.
/// </summary>
public object Data { get; set; }
} }
} }

View File

@ -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 // 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. // 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) if (matches == null)
{ {

View File

@ -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<DefaultEndpointFinder>.Instance);
}
private class HomeController
{
public void Index() { }
public void Contact() { }
}
private class AdminController
{
public void Index() { }
public void Contact() { }
}
}
}

View File

@ -4,10 +4,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Routing.EndpointFinders;
using Microsoft.AspNetCore.Routing.Internal; using Microsoft.AspNetCore.Routing.Internal;
using Microsoft.AspNetCore.Routing.Matchers; using Microsoft.AspNetCore.Routing.Matchers;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.ObjectPool;
using Moq; using Moq;
using Xunit; using Xunit;
@ -21,11 +21,11 @@ namespace Microsoft.AspNetCore.Routing
{ {
// Arrange // Arrange
var endpoint = CreateEndpoint("{controller}"); var endpoint = CreateEndpoint("{controller}");
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); var linkGenerator = CreateLinkGenerator();
var context = CreateLinkGeneratorContext(new { controller = "Home" }); var context = CreateRouteValuesContext(new { controller = "Home" });
// Act // Act
var link = linkGenerator.GetLink(context); var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// Assert // Assert
Assert.Equal("/Home", link); Assert.Equal("/Home", link);
@ -37,11 +37,12 @@ namespace Microsoft.AspNetCore.Routing
// Arrange // Arrange
var expectedMessage = "Could not find a matching endpoint to generate a link."; var expectedMessage = "Could not find a matching endpoint to generate a link.";
var endpoint = CreateEndpoint("{controller}/{action}"); var endpoint = CreateEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); var linkGenerator = CreateLinkGenerator();
var context = CreateLinkGeneratorContext(new { controller = "Home" }); var context = CreateRouteValuesContext(new { controller = "Home" });
// Act & Assert // Act & Assert
var exception = Assert.Throws<InvalidOperationException>(() => linkGenerator.GetLink(context)); var exception = Assert.Throws<InvalidOperationException>(
() => linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues));
Assert.Equal(expectedMessage, exception.Message); Assert.Equal(expectedMessage, exception.Message);
} }
@ -50,11 +51,15 @@ namespace Microsoft.AspNetCore.Routing
{ {
// Arrange // Arrange
var endpoint = CreateEndpoint("{controller}/{action}"); var endpoint = CreateEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); var linkGenerator = CreateLinkGenerator();
var context = CreateLinkGeneratorContext(new { controller = "Home" }); var context = CreateRouteValuesContext(new { controller = "Home" });
// Act // Act
var canGenerateLink = linkGenerator.TryGetLink(context, out var link); var canGenerateLink = linkGenerator.TryGetLink(
new[] { endpoint },
context.ExplicitValues,
context.AmbientValues,
out var link);
// Assert // Assert
Assert.False(canGenerateLink); Assert.False(canGenerateLink);
@ -68,11 +73,14 @@ namespace Microsoft.AspNetCore.Routing
var endpoint1 = CreateEndpoint("{controller}/{action}/{id?}"); var endpoint1 = CreateEndpoint("{controller}/{action}/{id?}");
var endpoint2 = CreateEndpoint("{controller}/{action}"); var endpoint2 = CreateEndpoint("{controller}/{action}");
var endpoint3 = CreateEndpoint("{controller}"); var endpoint3 = CreateEndpoint("{controller}");
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint1, endpoint2, endpoint3)); var linkGenerator = CreateLinkGenerator();
var context = CreateLinkGeneratorContext(new { controller = "Home", action = "Index", id = "10" }); var context = CreateRouteValuesContext(new { controller = "Home", action = "Index", id = "10" });
// Act // Act
var link = linkGenerator.GetLink(context); var link = linkGenerator.GetLink(
new[] { endpoint1, endpoint2, endpoint3 },
context.ExplicitValues,
context.AmbientValues);
// Assert // Assert
Assert.Equal("/Home/Index/10", link); Assert.Equal("/Home/Index/10", link);
@ -85,11 +93,14 @@ namespace Microsoft.AspNetCore.Routing
var endpoint1 = CreateEndpoint("{controller}/{action}/{id}"); var endpoint1 = CreateEndpoint("{controller}/{action}/{id}");
var endpoint2 = CreateEndpoint("{controller}/{action}"); var endpoint2 = CreateEndpoint("{controller}/{action}");
var endpoint3 = CreateEndpoint("{controller}"); var endpoint3 = CreateEndpoint("{controller}");
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint1, endpoint2, endpoint3)); var linkGenerator = CreateLinkGenerator();
var context = CreateLinkGeneratorContext(new { controller = "Home", action = "Index" }); var context = CreateRouteValuesContext(new { controller = "Home", action = "Index" });
// Act // Act
var link = linkGenerator.GetLink(context); var link = linkGenerator.GetLink(
new[] { endpoint1, endpoint2, endpoint3 },
context.ExplicitValues,
context.AmbientValues);
// Assert // Assert
Assert.Equal("/Home/Index", link); Assert.Equal("/Home/Index", link);
@ -100,13 +111,13 @@ namespace Microsoft.AspNetCore.Routing
{ {
// Arrange // Arrange
var endpoint = CreateEndpoint("{controller}/{action}"); var endpoint = CreateEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); var linkGenerator = CreateLinkGenerator();
var context = CreateLinkGeneratorContext( var context = CreateRouteValuesContext(
suppliedValues: new { name = "name with %special #characters" }, suppliedValues: new { name = "name with %special #characters" },
ambientValues: new { controller = "Home", action = "Index" }); ambientValues: new { controller = "Home", action = "Index" });
// Act // Act
var link = linkGenerator.GetLink(context); var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// Assert // Assert
Assert.Equal("/Home/Index?name=name%20with%20%25special%20%23characters", link); Assert.Equal("/Home/Index?name=name%20with%20%25special%20%23characters", link);
@ -117,13 +128,13 @@ namespace Microsoft.AspNetCore.Routing
{ {
// Arrange // Arrange
var endpoint = CreateEndpoint("{controller}/{action}"); var endpoint = CreateEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); var linkGenerator = CreateLinkGenerator();
var context = CreateLinkGeneratorContext( var context = CreateRouteValuesContext(
new { color = new List<string> { "red", "green", "blue" } }, new { color = new List<string> { "red", "green", "blue" } },
new { controller = "Home", action = "Index" }); new { controller = "Home", action = "Index" });
// Act // Act
var link = linkGenerator.GetLink(context); var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// Assert // Assert
Assert.Equal("/Home/Index?color=red&color=green&color=blue", link); Assert.Equal("/Home/Index?color=red&color=green&color=blue", link);
@ -134,13 +145,13 @@ namespace Microsoft.AspNetCore.Routing
{ {
// Arrange // Arrange
var endpoint = CreateEndpoint("{controller}/{action}"); var endpoint = CreateEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); var linkGenerator = CreateLinkGenerator();
var context = CreateLinkGeneratorContext( var context = CreateRouteValuesContext(
new { items = new List<int> { 10, 20, 30 } }, new { items = new List<int> { 10, 20, 30 } },
new { controller = "Home", action = "Index" }); new { controller = "Home", action = "Index" });
// Act // Act
var link = linkGenerator.GetLink(context); var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// Assert // Assert
Assert.Equal("/Home/Index?items=10&items=20&items=30", link); Assert.Equal("/Home/Index?items=10&items=20&items=30", link);
@ -151,13 +162,13 @@ namespace Microsoft.AspNetCore.Routing
{ {
// Arrange // Arrange
var endpoint = CreateEndpoint("{controller}/{action}"); var endpoint = CreateEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); var linkGenerator = CreateLinkGenerator();
var context = CreateLinkGeneratorContext( var context = CreateRouteValuesContext(
new { color = new List<string> { } }, new { color = new List<string> { } },
new { controller = "Home", action = "Index" }); new { controller = "Home", action = "Index" });
// Act // Act
var link = linkGenerator.GetLink(context); var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// Assert // Assert
Assert.Equal("/Home/Index", link); Assert.Equal("/Home/Index", link);
@ -168,13 +179,13 @@ namespace Microsoft.AspNetCore.Routing
{ {
// Arrange // Arrange
var endpoint = CreateEndpoint("{controller}/{action}"); var endpoint = CreateEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); var linkGenerator = CreateLinkGenerator();
var context = CreateLinkGeneratorContext( var context = CreateRouteValuesContext(
new { page = 1, color = new List<string> { "red", "green", "blue" }, message = "textfortest" }, new { page = 1, color = new List<string> { "red", "green", "blue" }, message = "textfortest" },
new { controller = "Home", action = "Index" }); new { controller = "Home", action = "Index" });
// Act // Act
var link = linkGenerator.GetLink(context); var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// Assert // Assert
Assert.Equal("/Home/Index?page=1&color=red&color=green&color=blue&message=textfortest", link); Assert.Equal("/Home/Index?page=1&color=red&color=green&color=blue&message=textfortest", link);
@ -185,13 +196,13 @@ namespace Microsoft.AspNetCore.Routing
{ {
// Arrange // Arrange
var endpoint = CreateEndpoint("{controller}/{action}"); var endpoint = CreateEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); var linkGenerator = CreateLinkGenerator();
var context = CreateLinkGeneratorContext( var context = CreateRouteValuesContext(
suppliedValues: new { action = "Index" }, suppliedValues: new { action = "Index" },
ambientValues: new { controller = "Home" }); ambientValues: new { controller = "Home" });
// Act // Act
var link = linkGenerator.GetLink(context); var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// Assert // Assert
Assert.Equal("/Home/Index", link); Assert.Equal("/Home/Index", link);
@ -201,9 +212,9 @@ namespace Microsoft.AspNetCore.Routing
//public void RouteGenerationRejectsConstraints() //public void RouteGenerationRejectsConstraints()
//{ //{
// // Arrange // // Arrange
// var context = CreateLinkGeneratorContext(new { p1 = "abcd" }); // var context = CreateRouteValuesContext(new { p1 = "abcd" });
// var endpoint = CreateEndpoint( // var endpoint = CreateEndpoints(
// "{p1}/{p2}", // "{p1}/{p2}",
// new { p2 = "catchall" }, // new { p2 = "catchall" },
// true, // true,
@ -220,16 +231,16 @@ namespace Microsoft.AspNetCore.Routing
//public void RouteGenerationAcceptsConstraints() //public void RouteGenerationAcceptsConstraints()
//{ //{
// // Arrange // // 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}", // "{p1}/{p2}",
// new { p2 = "catchall" }, // new { p2 = "catchall" },
// true, // true,
// new RouteValueDictionary(new { p2 = "\\d{4}" })); // new RouteValueDictionary(new { p2 = "\\d{4}" }));
// // Act // // Act
// var link = linkGenerator.GetLink(context); // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// // Assert // // Assert
// Assert.NotNull(pathData); // Assert.NotNull(pathData);
@ -242,9 +253,9 @@ namespace Microsoft.AspNetCore.Routing
//public void RouteWithCatchAllRejectsConstraints() //public void RouteWithCatchAllRejectsConstraints()
//{ //{
// // Arrange // // Arrange
// var context = CreateLinkGeneratorContext(new { p1 = "abcd" }); // var context = CreateRouteValuesContext(new { p1 = "abcd" });
// var endpoint = CreateEndpoint( // var endpoint = CreateEndpoints(
// "{p1}/{*p2}", // "{p1}/{*p2}",
// new { p2 = "catchall" }, // new { p2 = "catchall" },
// true, // true,
@ -261,16 +272,16 @@ namespace Microsoft.AspNetCore.Routing
//public void RouteWithCatchAllAcceptsConstraints() //public void RouteWithCatchAllAcceptsConstraints()
//{ //{
// // Arrange // // 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}", // "{p1}/{*p2}",
// new { p2 = "catchall" }, // new { p2 = "catchall" },
// true, // true,
// new RouteValueDictionary(new { p2 = "\\d{4}" })); // new RouteValueDictionary(new { p2 = "\\d{4}" }));
// // Act // // Act
// var link = linkGenerator.GetLink(context); // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// // Assert // // Assert
// Assert.NotNull(pathData); // Assert.NotNull(pathData);
@ -283,7 +294,7 @@ namespace Microsoft.AspNetCore.Routing
//public void GetLinkWithNonParameterConstraintReturnsUrlWithoutQueryString() //public void GetLinkWithNonParameterConstraintReturnsUrlWithoutQueryString()
//{ //{
// // Arrange // // Arrange
// var context = CreateLinkGeneratorContext(new { p1 = "hello", p2 = "1234" }); // var context = CreateRouteValuesContext(new { p1 = "hello", p2 = "1234" });
// var target = new Mock<IRouteConstraint>(); // var target = new Mock<IRouteConstraint>();
// target // target
@ -297,14 +308,14 @@ namespace Microsoft.AspNetCore.Routing
// .Returns(true) // .Returns(true)
// .Verifiable(); // .Verifiable();
// var endpoint = CreateEndpoint( // var endpoint = CreateEndpoints(
// "{p1}/{p2}", // "{p1}/{p2}",
// new { p2 = "catchall" }, // new { p2 = "catchall" },
// true, // true,
// new RouteValueDictionary(new { p2 = target.Object })); // new RouteValueDictionary(new { p2 = target.Object }));
// // Act // // Act
// var link = linkGenerator.GetLink(context); // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// // Assert // // Assert
// Assert.NotNull(pathData); // Assert.NotNull(pathData);
@ -322,13 +333,13 @@ namespace Microsoft.AspNetCore.Routing
//{ //{
// // Arrange // // Arrange
// var constraint = new CapturingConstraint(); // var constraint = new CapturingConstraint();
// var endpoint = CreateEndpoint( // var endpoint = CreateEndpoints(
// template: "slug/{controller}/{action}", // template: "slug/{controller}/{action}",
// defaultValues: null, // defaultValues: null,
// handleRequest: true, // handleRequest: true,
// constraints: new { c = constraint }); // constraints: new { c = constraint });
// var context = CreateLinkGeneratorContext( // var context = CreateRouteValuesContext(
// values: new { action = "Store" }, // values: new { action = "Store" },
// ambientValues: new { Controller = "Home", action = "Blog", extra = "42" }); // ambientValues: new { Controller = "Home", action = "Blog", extra = "42" });
@ -336,7 +347,7 @@ namespace Microsoft.AspNetCore.Routing
// new { controller = "Home", action = "Store", extra = "42" }); // new { controller = "Home", action = "Store", extra = "42" });
// // Act // // Act
// var link = linkGenerator.GetLink(context); // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// // Assert // // Assert
// Assert.Equal("/slug/Home/Store", link); // Assert.Equal("/slug/Home/Store", link);
@ -353,13 +364,13 @@ namespace Microsoft.AspNetCore.Routing
//{ //{
// // Arrange // // Arrange
// var constraint = new CapturingConstraint(); // var constraint = new CapturingConstraint();
// var endpoint = CreateEndpoint( // var endpoint = CreateEndpoints(
// template: "slug/{controller}/{action}", // template: "slug/{controller}/{action}",
// defaultValues: new { otherthing = "17" }, // defaultValues: new { otherthing = "17" },
// handleRequest: true, // handleRequest: true,
// constraints: new { c = constraint }); // constraints: new { c = constraint });
// var context = CreateLinkGeneratorContext( // var context = CreateRouteValuesContext(
// values: new { action = "Store" }, // values: new { action = "Store" },
// ambientValues: new { Controller = "Home", action = "Blog" }); // ambientValues: new { Controller = "Home", action = "Blog" });
@ -367,7 +378,7 @@ namespace Microsoft.AspNetCore.Routing
// new { controller = "Home", action = "Store" }); // new { controller = "Home", action = "Store" });
// // Act // // Act
// var link = linkGenerator.GetLink(context); // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// // Assert // // Assert
// Assert.Equal("/slug/Home/Store", link); // Assert.Equal("/slug/Home/Store", link);
@ -383,13 +394,13 @@ namespace Microsoft.AspNetCore.Routing
//{ //{
// // Arrange // // Arrange
// var constraint = new CapturingConstraint(); // var constraint = new CapturingConstraint();
// var endpoint = CreateEndpoint( // var endpoint = CreateEndpoints(
// template: "slug/{controller}/{action}", // template: "slug/{controller}/{action}",
// defaultValues: new { action = "Index" }, // defaultValues: new { action = "Index" },
// handleRequest: true, // handleRequest: true,
// constraints: new { c = constraint }); // constraints: new { c = constraint });
// var context = CreateLinkGeneratorContext( // var context = CreateRouteValuesContext(
// values: new { controller = "Shopping" }, // values: new { controller = "Shopping" },
// ambientValues: new { Controller = "Home", action = "Blog" }); // ambientValues: new { Controller = "Home", action = "Blog" });
@ -397,7 +408,7 @@ namespace Microsoft.AspNetCore.Routing
// new { controller = "Shopping", action = "Index" }); // new { controller = "Shopping", action = "Index" });
// // Act // // Act
// var link = linkGenerator.GetLink(context); // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// // Assert // // Assert
// Assert.Equal("/slug/Shopping", link); // Assert.Equal("/slug/Shopping", link);
@ -414,13 +425,13 @@ namespace Microsoft.AspNetCore.Routing
//{ //{
// // Arrange // // Arrange
// var constraint = new CapturingConstraint(); // var constraint = new CapturingConstraint();
// var endpoint = CreateEndpoint( // var endpoint = CreateEndpoints(
// template: "slug/{controller}/{action}", // template: "slug/{controller}/{action}",
// defaultValues: new { otherthing = "17", thirdthing = "13" }, // defaultValues: new { otherthing = "17", thirdthing = "13" },
// handleRequest: true, // handleRequest: true,
// constraints: new { c = constraint }); // constraints: new { c = constraint });
// var context = CreateLinkGeneratorContext( // var context = CreateRouteValuesContext(
// values: new { action = "Store", thirdthing = "13" }, // values: new { action = "Store", thirdthing = "13" },
// ambientValues: new { Controller = "Home", action = "Blog", otherthing = "17" }); // 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" }); // new { controller = "Home", action = "Store", otherthing = "17", thirdthing = "13" });
// // Act // // Act
// var link = linkGenerator.GetLink(context); // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// // Assert // // Assert
// Assert.Equal("/slug/Home/Store", link); // Assert.Equal("/slug/Home/Store", link);
@ -442,12 +453,12 @@ namespace Microsoft.AspNetCore.Routing
//public void GetLink_InlineConstraints_Success() //public void GetLink_InlineConstraints_Success()
//{ //{
// // Arrange // // Arrange
// var endpoint = CreateEndpoint("{controller}/{action}/{id:int}"); // var endpoint = CreateEndpoints("{controller}/{action}/{id:int}");
// var context = CreateLinkGeneratorContext( // var context = CreateRouteValuesContext(
// values: new { action = "Index", controller = "Home", id = 4 }); // values: new { action = "Index", controller = "Home", id = 4 });
// // Act // // Act
// var link = linkGenerator.GetLink(context); // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// // Assert // // Assert
// Assert.Equal("/Home/Index/4", link); // Assert.Equal("/Home/Index/4", link);
@ -459,8 +470,8 @@ namespace Microsoft.AspNetCore.Routing
//public void GetLink_InlineConstraints_NonMatchingvalue() //public void GetLink_InlineConstraints_NonMatchingvalue()
//{ //{
// // Arrange // // Arrange
// var endpoint = CreateEndpoint("{controller}/{action}/{id:int}"); // var endpoint = CreateEndpoints("{controller}/{action}/{id:int}");
// var context = CreateLinkGeneratorContext( // var context = CreateRouteValuesContext(
// values: new { action = "Index", controller = "Home", id = "asf" }); // values: new { action = "Index", controller = "Home", id = "asf" });
// // Act // // Act
@ -474,12 +485,12 @@ namespace Microsoft.AspNetCore.Routing
//public void GetLink_InlineConstraints_OptionalParameter_ValuePresent() //public void GetLink_InlineConstraints_OptionalParameter_ValuePresent()
//{ //{
// // Arrange // // Arrange
// var endpoint = CreateEndpoint("{controller}/{action}/{id:int?}"); // var endpoint = CreateEndpoints("{controller}/{action}/{id:int?}");
// var context = CreateLinkGeneratorContext( // var context = CreateRouteValuesContext(
// values: new { action = "Index", controller = "Home", id = 98 }); // values: new { action = "Index", controller = "Home", id = 98 });
// // Act // // Act
// var link = linkGenerator.GetLink(context); // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// // Assert // // Assert
// Assert.Equal("/Home/Index/98", link); // Assert.Equal("/Home/Index/98", link);
@ -491,12 +502,12 @@ namespace Microsoft.AspNetCore.Routing
//public void GetLink_InlineConstraints_OptionalParameter_ValueNotPresent() //public void GetLink_InlineConstraints_OptionalParameter_ValueNotPresent()
//{ //{
// // Arrange // // Arrange
// var endpoint = CreateEndpoint("{controller}/{action}/{id:int?}"); // var endpoint = CreateEndpoints("{controller}/{action}/{id:int?}");
// var context = CreateLinkGeneratorContext( // var context = CreateRouteValuesContext(
// values: new { action = "Index", controller = "Home" }); // values: new { action = "Index", controller = "Home" });
// // Act // // Act
// var link = linkGenerator.GetLink(context); // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// // Assert // // Assert
// Assert.Equal("/Home/Index", link); // Assert.Equal("/Home/Index", link);
@ -508,8 +519,8 @@ namespace Microsoft.AspNetCore.Routing
//public void GetLink_InlineConstraints_OptionalParameter_ValuePresent_ConstraintFails() //public void GetLink_InlineConstraints_OptionalParameter_ValuePresent_ConstraintFails()
//{ //{
// // Arrange // // Arrange
// var endpoint = CreateEndpoint("{controller}/{action}/{id:int?}"); // var endpoint = CreateEndpoints("{controller}/{action}/{id:int?}");
// var context = CreateLinkGeneratorContext( // var context = CreateRouteValuesContext(
// values: new { action = "Index", controller = "Home", id = "sdfd" }); // values: new { action = "Index", controller = "Home", id = "sdfd" });
// // Act // // Act
@ -523,12 +534,12 @@ namespace Microsoft.AspNetCore.Routing
//public void GetLink_InlineConstraints_CompositeInlineConstraint() //public void GetLink_InlineConstraints_CompositeInlineConstraint()
//{ //{
// // Arrange // // Arrange
// var endpoint = CreateEndpoint("{controller}/{action}/{id:int:range(1,20)}"); // var endpoint = CreateEndpoints("{controller}/{action}/{id:int:range(1,20)}");
// var context = CreateLinkGeneratorContext( // var context = CreateRouteValuesContext(
// values: new { action = "Index", controller = "Home", id = 14 }); // values: new { action = "Index", controller = "Home", id = 14 });
// // Act // // Act
// var link = linkGenerator.GetLink(context); // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// // Assert // // Assert
// Assert.Equal("/Home/Index/14", link); // Assert.Equal("/Home/Index/14", link);
@ -541,17 +552,17 @@ namespace Microsoft.AspNetCore.Routing
//{ //{
// // Arrange // // Arrange
// var constraint = new MaxLengthRouteConstraint(20); // var constraint = new MaxLengthRouteConstraint(20);
// var endpoint = CreateEndpoint( // var endpoint = CreateEndpoints(
// template: "{controller}/{action}/{name:alpha}", // template: "{controller}/{action}/{name:alpha}",
// defaultValues: null, // defaultValues: null,
// handleRequest: true, // handleRequest: true,
// constraints: new { name = constraint }); // constraints: new { name = constraint });
// var context = CreateLinkGeneratorContext( // var context = CreateRouteValuesContext(
// values: new { action = "Index", controller = "Home", name = "products" }); // values: new { action = "Index", controller = "Home", name = "products" });
// // Act // // Act
// var link = linkGenerator.GetLink(context); // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// // Assert // // Assert
// Assert.Equal("/Home/Index/products", link); // Assert.Equal("/Home/Index/products", link);
@ -564,12 +575,12 @@ namespace Microsoft.AspNetCore.Routing
{ {
// Arrange // Arrange
var endpoint = CreateEndpoint("{controller}/{action}/{name?}"); var endpoint = CreateEndpoint("{controller}/{action}/{name?}");
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); var linkGenerator = CreateLinkGenerator();
var context = CreateLinkGeneratorContext( var context = CreateRouteValuesContext(
suppliedValues: new { action = "Index", controller = "Home", name = "products" }); suppliedValues: new { action = "Index", controller = "Home", name = "products" });
// Act // Act
var link = linkGenerator.GetLink(context); var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// Assert // Assert
Assert.Equal("/Home/Index/products", link); Assert.Equal("/Home/Index/products", link);
@ -580,12 +591,12 @@ namespace Microsoft.AspNetCore.Routing
{ {
// Arrange // Arrange
var endpoint = CreateEndpoint("{controller}/{action}/{name?}"); var endpoint = CreateEndpoint("{controller}/{action}/{name?}");
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); var linkGenerator = CreateLinkGenerator();
var context = CreateLinkGeneratorContext( var context = CreateRouteValuesContext(
suppliedValues: new { action = "Index", controller = "Home" }); suppliedValues: new { action = "Index", controller = "Home" });
// Act // Act
var link = linkGenerator.GetLink(context); var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// Assert // Assert
Assert.Equal("/Home/Index", link); Assert.Equal("/Home/Index", link);
@ -598,12 +609,12 @@ namespace Microsoft.AspNetCore.Routing
var endpoint = CreateEndpoint( var endpoint = CreateEndpoint(
template: "{controller}/{action}/{name?}", template: "{controller}/{action}/{name?}",
defaultValues: new { name = "default-products" }); defaultValues: new { name = "default-products" });
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); var linkGenerator = CreateLinkGenerator();
var context = CreateLinkGeneratorContext( var context = CreateRouteValuesContext(
suppliedValues: new { action = "Index", controller = "Home", name = "products" }); suppliedValues: new { action = "Index", controller = "Home", name = "products" });
// Act // Act
var link = linkGenerator.GetLink(context); var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// Assert // Assert
Assert.Equal("/Home/Index/products", link); Assert.Equal("/Home/Index/products", link);
@ -616,12 +627,12 @@ namespace Microsoft.AspNetCore.Routing
var endpoint = CreateEndpoint( var endpoint = CreateEndpoint(
template: "{controller}/{action}/{name?}", template: "{controller}/{action}/{name?}",
defaultValues: new { name = "products" }); defaultValues: new { name = "products" });
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); var linkGenerator = CreateLinkGenerator();
var context = CreateLinkGeneratorContext( var context = CreateRouteValuesContext(
suppliedValues: new { action = "Index", controller = "Home" }); suppliedValues: new { action = "Index", controller = "Home" });
// Act // Act
var link = linkGenerator.GetLink(context); var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// Assert // Assert
Assert.Equal("/Home/Index", link); Assert.Equal("/Home/Index", link);
@ -632,12 +643,12 @@ namespace Microsoft.AspNetCore.Routing
{ {
// Arrange // Arrange
var endpoint = CreateEndpoint("{controller}/{action}/{name}"); var endpoint = CreateEndpoint("{controller}/{action}/{name}");
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); var linkGenerator = CreateLinkGenerator();
var context = CreateLinkGeneratorContext( var context = CreateRouteValuesContext(
suppliedValues: new { action = "Index", controller = "Home", name = "products", format = "json" }); suppliedValues: new { action = "Index", controller = "Home", name = "products", format = "json" });
// Act // Act
var link = linkGenerator.GetLink(context); var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// Assert // Assert
Assert.Equal("/Home/Index/products?format=json", link); Assert.Equal("/Home/Index/products?format=json", link);
@ -647,17 +658,17 @@ namespace Microsoft.AspNetCore.Routing
//public void GetLink_OptionalParameter_FollowedByDotAfterSlash_ParameterPresent() //public void GetLink_OptionalParameter_FollowedByDotAfterSlash_ParameterPresent()
//{ //{
// // Arrange // // Arrange
// var endpoint = CreateEndpoint( // var endpoint = CreateEndpoints(
// template: "{controller}/{action}/.{name?}", // template: "{controller}/{action}/.{name?}",
// defaultValues: null, // defaultValues: null,
// handleRequest: true, // handleRequest: true,
// constraints: null); // constraints: null);
// var context = CreateLinkGeneratorContext( // var context = CreateRouteValuesContext(
// values: new { action = "Index", controller = "Home", name = "products" }); // values: new { action = "Index", controller = "Home", name = "products" });
// // Act // // Act
// var link = linkGenerator.GetLink(context); // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// // Assert // // Assert
// Assert.Equal("/Home/Index/.products", link); // Assert.Equal("/Home/Index/.products", link);
@ -669,17 +680,17 @@ namespace Microsoft.AspNetCore.Routing
//public void GetLink_OptionalParameter_FollowedByDotAfterSlash_ParameterNotPresent() //public void GetLink_OptionalParameter_FollowedByDotAfterSlash_ParameterNotPresent()
//{ //{
// // Arrange // // Arrange
// var endpoint = CreateEndpoint( // var endpoint = CreateEndpoints(
// template: "{controller}/{action}/.{name?}", // template: "{controller}/{action}/.{name?}",
// defaultValues: null, // defaultValues: null,
// handleRequest: true, // handleRequest: true,
// constraints: null); // constraints: null);
// var context = CreateLinkGeneratorContext( // var context = CreateRouteValuesContext(
// values: new { action = "Index", controller = "Home" }); // values: new { action = "Index", controller = "Home" });
// // Act // // Act
// var link = linkGenerator.GetLink(context); // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// // Assert // // Assert
// Assert.Equal("/Home/Index/", link); // Assert.Equal("/Home/Index/", link);
@ -692,12 +703,12 @@ namespace Microsoft.AspNetCore.Routing
{ {
// Arrange // Arrange
var endpoint = CreateEndpoint("{controller}/{action}/{name?}"); var endpoint = CreateEndpoint("{controller}/{action}/{name?}");
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); var linkGenerator = CreateLinkGenerator();
var context = CreateLinkGeneratorContext( var context = CreateRouteValuesContext(
suppliedValues: new { action = "Index", controller = "Home" }); suppliedValues: new { action = "Index", controller = "Home" });
// Act // Act
var link = linkGenerator.GetLink(context); var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// Assert // Assert
Assert.Equal("/Home/Index", link); Assert.Equal("/Home/Index", link);
@ -707,14 +718,14 @@ namespace Microsoft.AspNetCore.Routing
//public void GetLink_TwoOptionalParameters_OneValueFromAmbientValues() //public void GetLink_TwoOptionalParameters_OneValueFromAmbientValues()
//{ //{
// // Arrange // // Arrange
// var endpoint = CreateEndpoint("a/{b=15}/{c?}/{d?}"); // var endpoint = CreateEndpoints("a/{b=15}/{c?}/{d?}");
// var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); // var linkGenerator = CreateLinkGenerator();
// var context = CreateLinkGeneratorContext( // var context = CreateRouteValuesContext(
// suppliedValues: new { }, // suppliedValues: new { },
// ambientValues: new { c = "17" }); // ambientValues: new { c = "17" });
// // Act // // Act
// var link = linkGenerator.GetLink(context); // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// // Assert // // Assert
// Assert.Equal("/a/15/17", link); // Assert.Equal("/a/15/17", link);
@ -724,14 +735,14 @@ namespace Microsoft.AspNetCore.Routing
//public void GetLink_OptionalParameterAfterDefault_OneValueFromAmbientValues() //public void GetLink_OptionalParameterAfterDefault_OneValueFromAmbientValues()
//{ //{
// // Arrange // // Arrange
// var endpoint = CreateEndpoint("a/{b=15}/{c?}"); // var endpoint = CreateEndpoints("a/{b=15}/{c?}");
// var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint)); // var linkGenerator = CreateLinkGenerator();
// var context = CreateLinkGeneratorContext( // var context = CreateRouteValuesContext(
// suppliedValues: new { }, // suppliedValues: new { },
// ambientValues: new { c = "17" }); // ambientValues: new { c = "17" });
// // Act // // Act
// var link = linkGenerator.GetLink(context); // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// // Assert // // Assert
// Assert.Equal("/a/15/17", link); // Assert.Equal("/a/15/17", link);
@ -741,18 +752,18 @@ namespace Microsoft.AspNetCore.Routing
//public void GetLink_TwoOptionalParametersAfterDefault_LastValueFromAmbientValues() //public void GetLink_TwoOptionalParametersAfterDefault_LastValueFromAmbientValues()
//{ //{
// // Arrange // // Arrange
// var endpoint = CreateEndpoint( // var endpoint = CreateEndpoints(
// template: "a/{b=15}/{c?}/{d?}", // template: "a/{b=15}/{c?}/{d?}",
// defaultValues: null, // defaultValues: null,
// handleRequest: true, // handleRequest: true,
// constraints: null); // constraints: null);
// var context = CreateLinkGeneratorContext( // var context = CreateRouteValuesContext(
// values: new { }, // values: new { },
// ambientValues: new { d = "17" }); // ambientValues: new { d = "17" });
// // Act // // Act
// var link = linkGenerator.GetLink(context); // var link = linkGenerator.GetLink(new[] { endpoint }, context.ExplicitValues, context.AmbientValues);
// // Assert // // Assert
// Assert.NotNull(pathData); // 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(); var context = new RouteValuesBasedEndpointFinderContext();
context.SuppliedValues = new RouteValueDictionary(suppliedValues); context.ExplicitValues = new RouteValueDictionary(suppliedValues);
context.AmbientValues = new RouteValueDictionary(ambientValues); context.AmbientValues = new RouteValueDictionary(ambientValues);
return context; return context;
} }
private MatcherEndpoint CreateEndpoint(string template, object defaultValues = null) private MatcherEndpoint CreateEndpoint(string template, object defaultValues = null)
{ {
var defaults = defaultValues == null ? new RouteValueDictionary() : new RouteValueDictionary(defaultValues);
return new MatcherEndpoint( return new MatcherEndpoint(
next => (httpContext) => Task.CompletedTask, next => (httpContext) => Task.CompletedTask,
template, template,
defaultValues, defaults,
new RouteValueDictionary(),
0, 0,
EndpointMetadataCollection.Empty, EndpointMetadataCollection.Empty,
null, null);
new Address("foo"));
} }
private ILinkGenerator CreateLinkGenerator(IEndpointFinder endpointFinder) private ILinkGenerator CreateLinkGenerator()
{ {
return new DefaultLinkGenerator( return new DefaultLinkGenerator(
endpointFinder,
new DefaultObjectPool<UriBuildingContext>(new UriBuilderContextPooledObjectPolicy()), new DefaultObjectPool<UriBuildingContext>(new UriBuilderContextPooledObjectPolicy()),
Mock.Of<ILogger<DefaultLinkGenerator>>()); Mock.Of<ILogger<DefaultLinkGenerator>>());
} }
private DefaultEndpointFinder CreateEndpointFinder(params Endpoint[] endpoints)
{
return new DefaultEndpointFinder(
new CompositeEndpointDataSource(new[] { new DefaultEndpointDataSource(endpoints) }),
NullLogger<DefaultEndpointFinder>.Instance);
}
} }
} }

View File

@ -24,13 +24,11 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
// Arrange // Arrange
var defaultEndpoint = new TestEndpoint( var defaultEndpoint = new TestEndpoint(
EndpointMetadataCollection.Empty, EndpointMetadataCollection.Empty,
"No constraint endpoint", "No constraint endpoint");
address: null);
var postEndpoint = new TestEndpoint( var postEndpoint = new TestEndpoint(
new EndpointMetadataCollection(new object[] { new HttpMethodEndpointConstraint(new[] { "POST" }) }), new EndpointMetadataCollection(new object[] { new HttpMethodEndpointConstraint(new[] { "POST" }) }),
"POST constraint endpoint", "POST constraint endpoint");
address: null);
var endpoints = new Endpoint[] var endpoints = new Endpoint[]
{ {
@ -62,13 +60,11 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
var defaultEndpoint1 = new TestEndpoint( var defaultEndpoint1 = new TestEndpoint(
EndpointMetadataCollection.Empty, EndpointMetadataCollection.Empty,
"Ambiguous1", "Ambiguous1");
address: null);
var defaultEndpoint2 = new TestEndpoint( var defaultEndpoint2 = new TestEndpoint(
EndpointMetadataCollection.Empty, EndpointMetadataCollection.Empty,
"Ambiguous2", "Ambiguous2");
address: null);
var endpoints = new Endpoint[] var endpoints = new Endpoint[]
{ {
@ -100,8 +96,8 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
var actions = new Endpoint[] var actions = new Endpoint[]
{ {
new TestEndpoint(EndpointMetadataCollection.Empty, "A1", address: null), new TestEndpoint(EndpointMetadataCollection.Empty, "A1"),
new TestEndpoint(EndpointMetadataCollection.Empty, "A2", address: null), new TestEndpoint(EndpointMetadataCollection.Empty, "A2"),
}; };
var selector = CreateSelector(actions, loggerFactory); var selector = CreateSelector(actions, loggerFactory);
@ -124,13 +120,11 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
// Arrange // Arrange
var actionWithConstraints = new TestEndpoint( var actionWithConstraints = new TestEndpoint(
new EndpointMetadataCollection(new[] { new HttpMethodEndpointConstraint(new string[] { "POST" }) }), new EndpointMetadataCollection(new[] { new HttpMethodEndpointConstraint(new string[] { "POST" }) }),
"Has constraint", "Has constraint");
address: null);
var actionWithoutConstraints = new TestEndpoint( var actionWithoutConstraints = new TestEndpoint(
EndpointMetadataCollection.Empty, EndpointMetadataCollection.Empty,
"No constraint", "No constraint");
address: null);
var actions = new Endpoint[] { actionWithConstraints, actionWithoutConstraints }; var actions = new Endpoint[] { actionWithConstraints, actionWithoutConstraints };
@ -150,13 +144,11 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
// Arrange // Arrange
var action1 = new TestEndpoint( var action1 = new TestEndpoint(
new EndpointMetadataCollection(new[] { new BooleanConstraint() { Pass = false, } }), new EndpointMetadataCollection(new[] { new BooleanConstraint() { Pass = false, } }),
"action1", "action1");
address: null);
var action2 = new TestEndpoint( var action2 = new TestEndpoint(
new EndpointMetadataCollection(new[] { new BooleanConstraint() { Pass = false, } }), new EndpointMetadataCollection(new[] { new BooleanConstraint() { Pass = false, } }),
"action2", "action2");
address: null);
var actions = new Endpoint[] { action1, 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 = false, Order = 0 },
new BooleanConstraint() { Pass = true, Order = 1 }, new BooleanConstraint() { Pass = true, Order = 1 },
}), }),
"action1", "action1");
address: null);
var action2 = new TestEndpoint(new EndpointMetadataCollection(new[] var action2 = new TestEndpoint(new EndpointMetadataCollection(new[]
{ {
new BooleanConstraint() { Pass = true, Order = 0 }, new BooleanConstraint() { Pass = true, Order = 0 },
new BooleanConstraint() { Pass = false, Order = 1 }, new BooleanConstraint() { Pass = false, Order = 1 },
}), }),
"action2", "action2");
address: null);
var actions = new Endpoint[] { action1, action2 }; var actions = new Endpoint[] { action1, action2 };
@ -213,13 +203,11 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
Constraint = new BooleanConstraint() { Pass = true }, Constraint = new BooleanConstraint() { Pass = true },
}, },
}), }),
"actionWithConstraints", "actionWithConstraints");
address: null);
var actionWithoutConstraints = new TestEndpoint( var actionWithoutConstraints = new TestEndpoint(
EndpointMetadataCollection.Empty, EndpointMetadataCollection.Empty,
"actionWithoutConstraints", "actionWithoutConstraints");
address: null);
var actions = new Endpoint[] { actionWithConstraints, actionWithoutConstraints }; var actions = new Endpoint[] { actionWithConstraints, actionWithoutConstraints };
@ -237,7 +225,7 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
public void SelectBestCandidate_MultipleCallsNoConstraint_ReturnsEndpoint() public void SelectBestCandidate_MultipleCallsNoConstraint_ReturnsEndpoint()
{ {
// Arrange // Arrange
var noConstraint = new TestEndpoint(EndpointMetadataCollection.Empty, "noConstraint", address: null); var noConstraint = new TestEndpoint(EndpointMetadataCollection.Empty, "noConstraint");
var actions = new Endpoint[] { noConstraint }; var actions = new Endpoint[] { noConstraint };
@ -261,8 +249,7 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
{ {
new object(), new object(),
}), }),
"noConstraint", "noConstraint");
address: null);
var actions = new Endpoint[] { noConstraint }; var actions = new Endpoint[] { noConstraint };
@ -286,8 +273,7 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
{ {
new ConstraintFactory(), new ConstraintFactory(),
}), }),
"nullConstraint", "nullConstraint");
address: null);
var actions = new Endpoint[] { nullConstraint }; var actions = new Endpoint[] { nullConstraint };
@ -312,13 +298,11 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
{ {
new BooleanConstraintMarker() { Pass = true }, new BooleanConstraintMarker() { Pass = true },
}), }),
"actionWithConstraints", "actionWithConstraints");
address: null);
var actionWithoutConstraints = new TestEndpoint( var actionWithoutConstraints = new TestEndpoint(
EndpointMetadataCollection.Empty, EndpointMetadataCollection.Empty,
"actionWithoutConstraints", "actionWithoutConstraints");
address: null);
var actions = new Endpoint[] { actionWithConstraints, actionWithoutConstraints, }; var actions = new Endpoint[] { actionWithConstraints, actionWithoutConstraints, };
@ -341,15 +325,13 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
{ {
new BooleanConstraint() { Pass = true, Order = 0, }, new BooleanConstraint() { Pass = true, Order = 0, },
}), }),
"best", "best");
address: null);
var worst = new TestEndpoint(new EndpointMetadataCollection(new[] var worst = new TestEndpoint(new EndpointMetadataCollection(new[]
{ {
new BooleanConstraint() { Pass = true, Order = 1, }, new BooleanConstraint() { Pass = true, Order = 1, },
}), }),
"worst", "worst");
address: null);
var actions = new Endpoint[] { best, 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 = 1, },
new BooleanConstraint() { Pass = true, Order = 2, }, new BooleanConstraint() { Pass = true, Order = 2, },
}), }),
"best", "best");
address: null);
var worst = new TestEndpoint(new EndpointMetadataCollection(new[] 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 = 1, },
new BooleanConstraint() { Pass = true, Order = 3, }, new BooleanConstraint() { Pass = true, Order = 3, },
}), }),
"worst", "worst");
address: null);
var actions = new Endpoint[] { best, 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 = true, Order = 1, },
new BooleanConstraint() { Pass = false, Order = 2, }, new BooleanConstraint() { Pass = false, Order = 2, },
}), }),
"nomatch1", "nomatch1");
address: null);
var nomatch2 = new TestEndpoint(new EndpointMetadataCollection(new[] 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 = true, Order = 1, },
new BooleanConstraint() { Pass = false, Order = 3, }, new BooleanConstraint() { Pass = false, Order = 3, },
}), }),
"nomatch2", "nomatch2");
address: null);
var best = new TestEndpoint(EndpointMetadataCollection.Empty, "best", address: null); var best = new TestEndpoint(EndpointMetadataCollection.Empty, "best");
var actions = new Endpoint[] { best, nomatch1, nomatch2 }; var actions = new Endpoint[] { best, nomatch1, nomatch2 };

View File

@ -62,7 +62,7 @@ namespace Microsoft.AspNetCore.Routing.Internal
var context = new EndpointConstraintContext(); var context = new EndpointConstraintContext();
var endpointSelectorCandidate = new EndpointSelectorCandidate( var endpointSelectorCandidate = new EndpointSelectorCandidate(
new TestEndpoint(EndpointMetadataCollection.Empty, string.Empty, address: null), new TestEndpoint(EndpointMetadataCollection.Empty, string.Empty),
new List<IEndpointConstraint> { constraint }); new List<IEndpointConstraint> { constraint });
context.Candidates = new List<EndpointSelectorCandidate> { endpointSelectorCandidate }; context.Candidates = new List<EndpointSelectorCandidate> { endpointSelectorCandidate };

View File

@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing
var context = CreateContext(new { }); var context = CreateContext(new { });
// Act // Act
var matches = tree.GetMatches(context); var matches = tree.GetMatches(context.Values, context.AmbientValues);
// Assert // Assert
Assert.Same(entry, Assert.Single(matches).Match); 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" }); var context = CreateContext(new { controller = "Store", action = "Buy" });
// Act // Act
var matches = tree.GetMatches(context); var matches = tree.GetMatches(context.Values, context.AmbientValues);
// Assert // Assert
Assert.Same(entry, Assert.Single(matches).Match); 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" }); var context = CreateContext(values: null, ambientValues: new { controller = "Store", action = "Buy" });
// Act // Act
var matches = tree.GetMatches(context); var matches = tree.GetMatches(context.Values, context.AmbientValues);
// Assert // Assert
var match = Assert.Single(matches); var match = Assert.Single(matches);
@ -90,7 +90,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing
ambientValues: new { controller = "Store", action = "Cart" }); ambientValues: new { controller = "Store", action = "Cart" });
// Act // Act
var matches = tree.GetMatches(context); var matches = tree.GetMatches(context.Values, context.AmbientValues);
// Assert // Assert
var match = Assert.Single(matches); var match = Assert.Single(matches);
@ -114,7 +114,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing
ambientValues: new { controller = "Store", action = "Buy" }); ambientValues: new { controller = "Store", action = "Buy" });
// Act // Act
var matches = tree.GetMatches(context); var matches = tree.GetMatches(context.Values, context.AmbientValues);
// Assert // Assert
var match = Assert.Single(matches); var match = Assert.Single(matches);
@ -136,7 +136,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing
var context = CreateContext(new { controller = "Store", action = "AddToCart" }); var context = CreateContext(new { controller = "Store", action = "AddToCart" });
// Act // Act
var matches = tree.GetMatches(context); var matches = tree.GetMatches(context.Values, context.AmbientValues);
// Assert // Assert
Assert.Empty(matches); Assert.Empty(matches);
@ -158,7 +158,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing
ambientValues: new { controller = "Store", action = "Cart" }); ambientValues: new { controller = "Store", action = "Cart" });
// Act // Act
var matches = tree.GetMatches(context); var matches = tree.GetMatches(context.Values, context.AmbientValues);
// Assert // Assert
Assert.Empty(matches); Assert.Empty(matches);
@ -183,7 +183,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing
ambientValues: new { controller = "Store", action = "Buy" }); ambientValues: new { controller = "Store", action = "Buy" });
// Act // Act
var matches = tree.GetMatches(context); var matches = tree.GetMatches(context.Values, context.AmbientValues);
// Assert // Assert
Assert.Same(entry1, Assert.Single(matches).Match); Assert.Same(entry1, Assert.Single(matches).Match);
@ -209,7 +209,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing
ambientValues: new { controller = "Store", action = "Buy" }); ambientValues: new { controller = "Store", action = "Buy" });
// Act // 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
Assert.Equal(entries, matches); Assert.Equal(entries, matches);
@ -233,7 +233,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing
var context = CreateContext(new { controller = "Store", action = "Buy", slug = "1234" }); var context = CreateContext(new { controller = "Store", action = "Buy", slug = "1234" });
// Act // 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
Assert.Equal(entries, matches); Assert.Equal(entries, matches);
@ -260,7 +260,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing
var context = CreateContext(new { controller = "Store", action = "Buy" }); var context = CreateContext(new { controller = "Store", action = "Buy" });
// Act // 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
Assert.Equal(entries, matches); Assert.Equal(entries, matches);
@ -286,7 +286,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing
var context = CreateContext(new { controller = "Store", action = "Buy" }); var context = CreateContext(new { controller = "Store", action = "Buy" });
// Act // 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
Assert.Equal(entries, matches); Assert.Equal(entries, matches);
@ -312,7 +312,7 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing
var context = CreateContext(new { controller = "Store", action = "Buy" }); var context = CreateContext(new { controller = "Store", action = "Buy" });
// Act // 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
Assert.Equal(entries, matches); Assert.Equal(entries, matches);

View File

@ -34,17 +34,18 @@ namespace Microsoft.AspNetCore.Routing.Matchers
internal static MatcherEndpoint CreateEndpoint( internal static MatcherEndpoint CreateEndpoint(
string template, string template,
object defaults = null, object defaultValues = null,
int? order = null) int? order = null)
{ {
var defaults = defaultValues == null ? new RouteValueDictionary() : new RouteValueDictionary(defaultValues);
return new MatcherEndpoint( return new MatcherEndpoint(
MatcherEndpoint.EmptyInvoker, MatcherEndpoint.EmptyInvoker,
template, template,
defaults, defaults,
new RouteValueDictionary(),
order ?? 0, order ?? 0,
EndpointMetadataCollection.Empty, EndpointMetadataCollection.Empty,
"endpoint: " + template, "endpoint: " + template);
address: null);
} }
internal (Matcher matcher, MatcherEndpoint endpoint) CreateMatcher(string template) internal (Matcher matcher, MatcherEndpoint endpoint) CreateMatcher(string template)

View File

@ -31,10 +31,10 @@ namespace Microsoft.AspNetCore.Routing.Matchers
// MatcherEndpoint.Values contains the default values parsed from the template // MatcherEndpoint.Values contains the default values parsed from the template
// as well as those specified with a literal. We need to separate those // as well as those specified with a literal. We need to separate those
// for legacy cases. // for legacy cases.
var defaults = new RouteValueDictionary(endpoint.Values); var defaults = endpoint.Defaults;
for (var i = 0; i < endpoint.ParsedTemlate.Parameters.Count; i++) 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) if (parameter.DefaultValue != null)
{ {
defaults.Remove(parameter.Name); defaults.Remove(parameter.Name);
@ -79,7 +79,7 @@ namespace Microsoft.AspNetCore.Routing.Matchers
return comparison; 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) if (comparison != 0)
{ {
return comparison; return comparison;

View File

@ -1,23 +1,22 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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.Http;
using Microsoft.AspNetCore.Routing.EndpointConstraints; using Microsoft.AspNetCore.Routing.EndpointConstraints;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Routing.Matchers namespace Microsoft.AspNetCore.Routing.Matchers
{ {
public class TreeMatcherTests 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) private TreeMatcher CreateTreeMatcher(EndpointDataSource endpointDataSource)

View File

@ -4,7 +4,6 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Routing.Internal; using Microsoft.AspNetCore.Routing.Internal;
using Microsoft.AspNetCore.Routing.Template;
using Microsoft.AspNetCore.Routing.Tree; using Microsoft.AspNetCore.Routing.Tree;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.ObjectPool;
@ -38,10 +37,10 @@ namespace Microsoft.AspNetCore.Routing.Matchers
// MatcherEndpoint.Values contains the default values parsed from the template // MatcherEndpoint.Values contains the default values parsed from the template
// as well as those specified with a literal. We need to separate those // as well as those specified with a literal. We need to separate those
// for legacy cases. // for legacy cases.
var defaults = new RouteValueDictionary(endpoint.Values); var defaults = endpoint.Defaults;
for (var i = 0; i < endpoint.ParsedTemlate.Parameters.Count; i++) 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)) if (parameter.DefaultValue == null && defaults.ContainsKey(parameter.Name))
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
@ -50,8 +49,8 @@ namespace Microsoft.AspNetCore.Routing.Matchers
} }
_inner.MapInbound( _inner.MapInbound(
handler, handler,
endpoint.ParsedTemlate, endpoint.ParsedTemplate,
routeName: null, routeName: null,
order: endpoint.Order); order: endpoint.Order);
} }

View File

@ -5,8 +5,8 @@ namespace Microsoft.AspNetCore.Routing.TestObjects
{ {
internal class TestEndpoint : Endpoint internal class TestEndpoint : Endpoint
{ {
public TestEndpoint(EndpointMetadataCollection metadata, string displayName, Address address) public TestEndpoint(EndpointMetadataCollection metadata, string displayName)
: base(metadata, displayName, address) : base(metadata, displayName)
{ {
} }
} }

View File

@ -1,8 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Matchers; using Microsoft.AspNetCore.Routing.Matchers;
@ -22,7 +20,7 @@ namespace Microsoft.AspNetCore.Routing.TestObjects
{ {
if (_isHandled) if (_isHandled)
{ {
feature.Endpoint = new TestEndpoint(EndpointMetadataCollection.Empty, "Test endpoint", address: null); feature.Endpoint = new TestEndpoint(EndpointMetadataCollection.Empty, "Test endpoint");
} }
return Task.CompletedTask; return Task.CompletedTask;