Merge release/2.2

This commit is contained in:
James Newton-King 2018-08-22 20:37:21 +12:00
commit aff4a38865
No known key found for this signature in database
GPG Key ID: 0A66B2F456BF5526
89 changed files with 575 additions and 502 deletions

View File

@ -2,9 +2,9 @@
// 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.Text; using System.Text;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Matching;
using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
@ -21,8 +21,8 @@ namespace Benchmarks
var endpointDataSource = new DefaultEndpointDataSource(new[] var endpointDataSource = new DefaultEndpointDataSource(new[]
{ {
new MatcherEndpoint( new RouteEndpoint(
invoker: (next) => (httpContext) => requestDelegate: (httpContext) =>
{ {
var response = httpContext.Response; var response = httpContext.Response;
var payloadLength = _helloWorldPayload.Length; var payloadLength = _helloWorldPayload.Length;
@ -47,4 +47,4 @@ namespace Benchmarks
app.UseEndpoint(); app.UseEndpoint();
} }
} }
} }

View File

@ -3,6 +3,7 @@
using System; using System;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing
{ {

View File

@ -5,7 +5,9 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing.Matching; using Microsoft.AspNetCore.Routing.Matching;
using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.AspNetCore.Routing.Template; using Microsoft.AspNetCore.Routing.Template;
@ -18,10 +20,10 @@ namespace Microsoft.AspNetCore.Routing
{ {
public abstract class EndpointRoutingBenchmarkBase public abstract class EndpointRoutingBenchmarkBase
{ {
private protected MatcherEndpoint[] Endpoints; private protected RouteEndpoint[] Endpoints;
private protected HttpContext[] Requests; private protected HttpContext[] Requests;
private protected void SetupEndpoints(params MatcherEndpoint[] endpoints) private protected void SetupEndpoints(params RouteEndpoint[] endpoints)
{ {
Endpoints = endpoints; Endpoints = endpoints;
} }
@ -76,8 +78,8 @@ namespace Microsoft.AspNetCore.Routing
var message = new StringBuilder(); var message = new StringBuilder();
message.AppendLine($"Validation failed for request {Array.IndexOf(Requests, httpContext)}"); message.AppendLine($"Validation failed for request {Array.IndexOf(Requests, httpContext)}");
message.AppendLine($"{httpContext.Request.Method} {httpContext.Request.Path}"); message.AppendLine($"{httpContext.Request.Method} {httpContext.Request.Path}");
message.AppendLine($"expected: '{((MatcherEndpoint)expected)?.DisplayName ?? "null"}'"); message.AppendLine($"expected: '{((RouteEndpoint)expected)?.DisplayName ?? "null"}'");
message.AppendLine($"actual: '{((MatcherEndpoint)actual)?.DisplayName ?? "null"}'"); message.AppendLine($"actual: '{((RouteEndpoint)actual)?.DisplayName ?? "null"}'");
throw new InvalidOperationException(message.ToString()); throw new InvalidOperationException(message.ToString());
} }
} }
@ -95,7 +97,7 @@ namespace Microsoft.AspNetCore.Routing
} }
} }
protected MatcherEndpoint CreateEndpoint( protected RouteEndpoint CreateEndpoint(
string template, string template,
object defaults = null, object defaults = null,
object constraints = null, object constraints = null,
@ -108,8 +110,8 @@ namespace Microsoft.AspNetCore.Routing
var endpointMetadata = new List<object>(metadata ?? Array.Empty<object>()); var endpointMetadata = new List<object>(metadata ?? Array.Empty<object>());
endpointMetadata.Add(new RouteValuesAddressMetadata(routeName, new RouteValueDictionary(requiredValues))); endpointMetadata.Add(new RouteValuesAddressMetadata(routeName, new RouteValueDictionary(requiredValues)));
return new MatcherEndpoint( return new RouteEndpoint(
MatcherEndpoint.EmptyInvoker, (context) => Task.CompletedTask,
RoutePatternFactory.Parse(template, defaults, constraints), RoutePatternFactory.Parse(template, defaults, constraints),
order, order,
new EndpointMetadataCollection(endpointMetadata), new EndpointMetadataCollection(endpointMetadata),
@ -119,14 +121,15 @@ namespace Microsoft.AspNetCore.Routing
protected (HttpContext httpContext, RouteValueDictionary ambientValues) CreateCurrentRequestContext( protected (HttpContext httpContext, RouteValueDictionary ambientValues) CreateCurrentRequestContext(
object ambientValues = null) object ambientValues = null)
{ {
var feature = new EndpointFeature { Values = new RouteValueDictionary(ambientValues) }; var feature = new EndpointFeature { RouteValues = new RouteValueDictionary(ambientValues) };
var context = new DefaultHttpContext(); var context = new DefaultHttpContext();
context.Features.Set<IEndpointFeature>(feature); context.Features.Set<IEndpointFeature>(feature);
context.Features.Set<IRouteValuesFeature>(feature);
return (context, feature.Values); return (context, feature.RouteValues);
} }
protected void CreateOutboundRouteEntry(TreeRouteBuilder treeRouteBuilder, MatcherEndpoint endpoint) protected void CreateOutboundRouteEntry(TreeRouteBuilder treeRouteBuilder, RouteEndpoint endpoint)
{ {
var routeValuesAddressMetadata = endpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>(); var routeValuesAddressMetadata = endpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
var requiredValues = routeValuesAddressMetadata?.RequiredValues ?? new RouteValueDictionary(); var requiredValues = routeValuesAddressMetadata?.RequiredValues ?? new RouteValueDictionary();

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Routing.LinkGeneration
private void SetupEndpoints() private void SetupEndpoints()
{ {
Endpoints = new MatcherEndpoint[243]; Endpoints = new RouteEndpoint[243];
Endpoints[0] = CreateEndpoint("/emojis", "Controller1", "Action1", "GET"); Endpoints[0] = CreateEndpoint("/emojis", "Controller1", "Action1", "GET");
Endpoints[1] = CreateEndpoint("/events", "Controller2", "Action1", "GET"); Endpoints[1] = CreateEndpoint("/events", "Controller2", "Action1", "GET");
Endpoints[2] = CreateEndpoint("/feeds", "Controller3", "Action1", "GET"); Endpoints[2] = CreateEndpoint("/feeds", "Controller3", "Action1", "GET");
@ -1485,7 +1485,7 @@ namespace Microsoft.AspNetCore.Routing.LinkGeneration
return builder.Build(); return builder.Build();
} }
private MatcherEndpoint CreateEndpoint(string template, string controllerName, string actionName, string httpMethod) private RouteEndpoint CreateEndpoint(string template, string controllerName, string actionName, string httpMethod)
{ {
var requiredValues = new var requiredValues = new
{ {

View File

@ -3,6 +3,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
{ {

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
private void SetupEndpoints() private void SetupEndpoints()
{ {
Endpoints = new MatcherEndpoint[5160]; Endpoints = new RouteEndpoint[5160];
Endpoints[0] = CreateEndpoint("/account", "GET"); Endpoints[0] = CreateEndpoint("/account", "GET");
Endpoints[1] = CreateEndpoint("/analyze", "POST"); Endpoints[1] = CreateEndpoint("/analyze", "POST");
Endpoints[2] = CreateEndpoint("/apis", "GET"); Endpoints[2] = CreateEndpoint("/apis", "GET");

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
private void SetupEndpoints() private void SetupEndpoints()
{ {
Endpoints = new MatcherEndpoint[3517]; Endpoints = new RouteEndpoint[3517];
Endpoints[0] = CreateEndpoint("/account"); Endpoints[0] = CreateEndpoint("/account");
Endpoints[1] = CreateEndpoint("/analyze"); Endpoints[1] = CreateEndpoint("/analyze");
Endpoints[2] = CreateEndpoint("/apis"); Endpoints[2] = CreateEndpoint("/apis");

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
private void SetupEndpoints() private void SetupEndpoints()
{ {
Endpoints = new MatcherEndpoint[155]; Endpoints = new RouteEndpoint[155];
Endpoints[0] = CreateEndpoint("/emojis"); Endpoints[0] = CreateEndpoint("/emojis");
Endpoints[1] = CreateEndpoint("/events"); Endpoints[1] = CreateEndpoint("/events");
Endpoints[2] = CreateEndpoint("/feeds"); Endpoints[2] = CreateEndpoint("/feeds");

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
[GlobalSetup] [GlobalSetup]
public void Setup() public void Setup()
{ {
Endpoints = new MatcherEndpoint[1]; Endpoints = new RouteEndpoint[1];
Endpoints[0] = CreateEndpoint("/plaintext"); Endpoints[0] = CreateEndpoint("/plaintext");
Requests = new HttpContext[1]; Requests = new HttpContext[1];

View File

@ -4,6 +4,7 @@
using System; using System;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
{ {
@ -32,7 +33,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
private void SetupEndpoints() private void SetupEndpoints()
{ {
Endpoints = new MatcherEndpoint[10]; Endpoints = new RouteEndpoint[10];
Endpoints[0] = CreateEndpoint("/another-really-cool-entry"); Endpoints[0] = CreateEndpoint("/another-really-cool-entry");
Endpoints[1] = CreateEndpoint("/Some-Entry"); Endpoints[1] = CreateEndpoint("/Some-Entry");
Endpoints[2] = CreateEndpoint("/a/path/with/more/segments"); Endpoints[2] = CreateEndpoint("/a/path/with/more/segments");

View File

@ -3,6 +3,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
{ {

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
private void SetupEndpoints() private void SetupEndpoints()
{ {
Endpoints = new MatcherEndpoint[243]; Endpoints = new RouteEndpoint[243];
Endpoints[0] = CreateEndpoint("/emojis", "GET"); Endpoints[0] = CreateEndpoint("/emojis", "GET");
Endpoints[1] = CreateEndpoint("/events", "GET"); Endpoints[1] = CreateEndpoint("/events", "GET");
Endpoints[2] = CreateEndpoint("/feeds", "GET"); Endpoints[2] = CreateEndpoint("/feeds", "GET");

View File

@ -4,6 +4,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
{ {
@ -22,7 +23,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
[GlobalSetup] [GlobalSetup]
public void Setup() public void Setup()
{ {
Endpoints = new MatcherEndpoint[1]; Endpoints = new RouteEndpoint[1];
Endpoints[0] = CreateEndpoint("/plaintext"); Endpoints[0] = CreateEndpoint("/plaintext");
Requests = new HttpContext[1]; Requests = new HttpContext[1];

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
{ {
@ -12,17 +13,17 @@ namespace Microsoft.AspNetCore.Routing.Matching
// to establish a lower bound for perf comparisons. // to establish a lower bound for perf comparisons.
internal sealed class TrivialMatcher : Matcher internal sealed class TrivialMatcher : Matcher
{ {
private readonly MatcherEndpoint _endpoint; private readonly RouteEndpoint _endpoint;
private readonly Candidate[] _candidates; private readonly Candidate[] _candidates;
public TrivialMatcher(MatcherEndpoint endpoint) public TrivialMatcher(RouteEndpoint endpoint)
{ {
_endpoint = endpoint; _endpoint = endpoint;
_candidates = new Candidate[] { new Candidate(endpoint), }; _candidates = new Candidate[] { new Candidate(endpoint), };
} }
public sealed override Task MatchAsync(HttpContext httpContext, IEndpointFeature feature) public sealed override Task MatchAsync(HttpContext httpContext, EndpointFeature feature)
{ {
if (httpContext == null) if (httpContext == null)
{ {
@ -38,7 +39,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
if (string.Equals(_endpoint.RoutePattern.RawText, path, StringComparison.OrdinalIgnoreCase)) if (string.Equals(_endpoint.RoutePattern.RawText, path, StringComparison.OrdinalIgnoreCase))
{ {
feature.Endpoint = _endpoint; feature.Endpoint = _endpoint;
feature.Values = new RouteValueDictionary(); feature.RouteValues = new RouteValueDictionary();
} }
return Task.CompletedTask; return Task.CompletedTask;

View File

@ -8,9 +8,9 @@ namespace Microsoft.AspNetCore.Routing.Matching
{ {
internal class TrivialMatcherBuilder : MatcherBuilder internal class TrivialMatcherBuilder : MatcherBuilder
{ {
private readonly List<MatcherEndpoint> _endpoints = new List<MatcherEndpoint>(); private readonly List<RouteEndpoint> _endpoints = new List<RouteEndpoint>();
public override void AddEndpoint(MatcherEndpoint endpoint) public override void AddEndpoint(RouteEndpoint endpoint)
{ {
_endpoints.Add(endpoint); _endpoints.Add(endpoint);
} }

View File

@ -78,7 +78,7 @@ namespace Microsoft.AspNetCore.Routing
private void SetupEndpoints() private void SetupEndpoints()
{{ {{
Endpoints = new MatcherEndpoint[{3}]; Endpoints = new RouteEndpoint[{3}];
{0} {0}
}} }}
@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.Routing
return builder.Build(); return builder.Build();
}} }}
private MatcherEndpoint CreateEndpoint(string template, string controllerName, string actionName, string httpMethod) private RouteEndpoint CreateEndpoint(string template, string controllerName, string actionName, string httpMethod)
{{ {{
var requiredValues = new var requiredValues = new
{{ {{

View File

@ -36,7 +36,7 @@ namespace RoutingSample.Web
builder.MapHello("/helloworld", "World"); builder.MapHello("/helloworld", "World");
builder.MapEndpoint( builder.MapEndpoint(
(next) => (httpContext) => (httpContext) =>
{ {
var response = httpContext.Response; var response = httpContext.Response;
var payloadLength = _homePayload.Length; var payloadLength = _homePayload.Length;
@ -48,7 +48,7 @@ namespace RoutingSample.Web
"/", "/",
"Home"); "Home");
builder.MapEndpoint( builder.MapEndpoint(
(next) => (httpContext) => (httpContext) =>
{ {
var response = httpContext.Response; var response = httpContext.Response;
var payloadLength = _plainTextPayload.Length; var payloadLength = _plainTextPayload.Length;
@ -60,7 +60,7 @@ namespace RoutingSample.Web
"/plaintext", "/plaintext",
"Plaintext"); "Plaintext");
builder.MapEndpoint( builder.MapEndpoint(
(next) => (httpContext) => (httpContext) =>
{ {
var response = httpContext.Response; var response = httpContext.Response;
response.StatusCode = 200; response.StatusCode = 200;
@ -70,7 +70,7 @@ namespace RoutingSample.Web
"/withconstraints/{id:endsWith(_001)}", "/withconstraints/{id:endsWith(_001)}",
"withconstraints"); "withconstraints");
builder.MapEndpoint( builder.MapEndpoint(
(next) => (httpContext) => (httpContext) =>
{ {
var response = httpContext.Response; var response = httpContext.Response;
response.StatusCode = 200; response.StatusCode = 200;
@ -80,7 +80,7 @@ namespace RoutingSample.Web
"/withoptionalconstraints/{id:endsWith(_001)?}", "/withoptionalconstraints/{id:endsWith(_001)?}",
"withoptionalconstraints"); "withoptionalconstraints");
builder.MapEndpoint( builder.MapEndpoint(
(next) => (httpContext) => (httpContext) =>
{ {
using (var writer = new StreamWriter(httpContext.Response.Body, Encoding.UTF8, 1024, leaveOpen: true)) using (var writer = new StreamWriter(httpContext.Response.Body, Encoding.UTF8, 1024, leaveOpen: true))
{ {
@ -95,7 +95,7 @@ namespace RoutingSample.Web
"DFA Graph", "DFA Graph",
new object[] { new HttpMethodMetadata(new[] { "GET", }) }); new object[] { new HttpMethodMetadata(new[] { "GET", }) });
builder.MapEndpoint( builder.MapEndpoint(
(next) => (httpContext) => (httpContext) =>
{ {
var linkGenerator = httpContext.RequestServices.GetRequiredService<LinkGenerator>(); var linkGenerator = httpContext.RequestServices.GetRequiredService<LinkGenerator>();
@ -112,7 +112,7 @@ namespace RoutingSample.Web
new RouteValuesAddressMetadata(name: "WithSingleAsteriskCatchAll", requiredValues: new RouteValueDictionary()), new RouteValuesAddressMetadata(name: "WithSingleAsteriskCatchAll", requiredValues: new RouteValueDictionary()),
}); });
builder.MapEndpoint( builder.MapEndpoint(
(next) => (httpContext) => (httpContext) =>
{ {
var linkGenerator = httpContext.RequestServices.GetRequiredService<LinkGenerator>(); var linkGenerator = httpContext.RequestServices.GetRequiredService<LinkGenerator>();

View File

@ -1,27 +1,30 @@
// 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.Http
{ {
/// <summary> /// <summary>
/// Respresents a logical endpoint in an application. /// Respresents a logical endpoint in an application.
/// </summary> /// </summary>
public abstract class Endpoint public class Endpoint
{ {
/// <summary> /// <summary>
/// Creates a new instance of <see cref="Endpoint"/>. /// Creates a new instance of <see cref="Endpoint"/>.
/// </summary> /// </summary>
/// <param name="requestDelegate">The delegate used to process requests for the endpoint.</param>
/// <param name="metadata"> /// <param name="metadata">
/// The endpoint <see cref="EndpointMetadataCollection"/>. May be null. /// The endpoint <see cref="EndpointMetadataCollection"/>. May be null.
/// </param> /// </param>
/// <param name="displayName"> /// <param name="displayName">
/// The informational display name of the endpoint. May be null. /// The informational display name of the endpoint. May be null.
/// </param> /// </param>
protected Endpoint( public Endpoint(
RequestDelegate requestDelegate,
EndpointMetadataCollection metadata, EndpointMetadataCollection metadata,
string displayName) string displayName)
{ {
// All are allowed to be null // All are allowed to be null
RequestDelegate = requestDelegate;
Metadata = metadata ?? EndpointMetadataCollection.Empty; Metadata = metadata ?? EndpointMetadataCollection.Empty;
DisplayName = displayName; DisplayName = displayName;
} }
@ -36,6 +39,11 @@ namespace Microsoft.AspNetCore.Routing
/// </summary> /// </summary>
public EndpointMetadataCollection Metadata { get; } public EndpointMetadataCollection Metadata { get; }
/// <summary>
/// Gets the delegate used to process requests for the endpoint.
/// </summary>
public RequestDelegate RequestDelegate { get; }
public override string ToString() => DisplayName ?? base.ToString(); public override string ToString() => DisplayName ?? base.ToString();
} }
} }

View File

@ -8,7 +8,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Http
{ {
/// <summary> /// <summary>
/// A collection of arbitrary metadata associated with an endpoint. /// A collection of arbitrary metadata associated with an endpoint.

View File

@ -1,10 +1,7 @@
// 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; namespace Microsoft.AspNetCore.Http.Features
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing
{ {
/// <summary> /// <summary>
/// A feature interface for endpoint routing. Use <see cref="HttpContext.Features"/> /// A feature interface for endpoint routing. Use <see cref="HttpContext.Features"/>
@ -13,21 +10,9 @@ namespace Microsoft.AspNetCore.Routing
public interface IEndpointFeature public interface IEndpointFeature
{ {
/// <summary> /// <summary>
/// Gets or sets the selected <see cref="Routing.Endpoint"/> for the current /// Gets or sets the selected <see cref="Http.Endpoint"/> for the current
/// request. /// request.
/// </summary> /// </summary>
Endpoint Endpoint { get; set; } Endpoint Endpoint { get; set; }
/// <summary>
/// Gets or sets a delegate that can be used to invoke the current
/// <see cref="Routing.Endpoint"/>.
/// </summary>
Func<RequestDelegate, RequestDelegate> Invoker { get; set; }
/// <summary>
/// Gets or sets the <see cref="RouteValueDictionary"/> associated with the currrent
/// request.
/// </summary>
RouteValueDictionary Values { get; set; }
} }
} }

View File

@ -0,0 +1,16 @@
// 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;
namespace Microsoft.AspNetCore.Http.Features
{
public interface IRouteValuesFeature
{
/// <summary>
/// Gets or sets the <see cref="RouteValueDictionary"/> associated with the currrent
/// request.
/// </summary>
RouteValueDictionary RouteValues { get; set; }
}
}

View File

@ -7,7 +7,7 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using Microsoft.AspNetCore.Routing.Matching; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing
@ -132,15 +132,15 @@ namespace Microsoft.AspNetCore.Routing
var sb = new StringBuilder(); var sb = new StringBuilder();
foreach (var endpoint in _endpoints) foreach (var endpoint in _endpoints)
{ {
if (endpoint is MatcherEndpoint matcherEndpoint) if (endpoint is RouteEndpoint routeEndpoint)
{ {
var template = matcherEndpoint.RoutePattern.RawText; var template = routeEndpoint.RoutePattern.RawText;
template = string.IsNullOrEmpty(template) ? "\"\"" : template; template = string.IsNullOrEmpty(template) ? "\"\"" : template;
sb.Append(template); sb.Append(template);
sb.Append(", Defaults: new { "); sb.Append(", Defaults: new { ");
sb.Append(string.Join(", ", FormatValues(matcherEndpoint.RoutePattern.Defaults))); sb.Append(string.Join(", ", FormatValues(routeEndpoint.RoutePattern.Defaults)));
sb.Append(" }"); sb.Append(" }");
var routeValuesAddressMetadata = matcherEndpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>(); var routeValuesAddressMetadata = routeEndpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
sb.Append(", Route Name: "); sb.Append(", Route Name: ");
sb.Append(routeValuesAddressMetadata?.Name); sb.Append(routeValuesAddressMetadata?.Name);
if (routeValuesAddressMetadata?.RequiredValues != null) if (routeValuesAddressMetadata?.RequiredValues != null)
@ -150,21 +150,21 @@ namespace Microsoft.AspNetCore.Routing
sb.Append(" }"); sb.Append(" }");
} }
sb.Append(", Order: "); sb.Append(", Order: ");
sb.Append(matcherEndpoint.Order); sb.Append(routeEndpoint.Order);
var httpMethodMetadata = matcherEndpoint.Metadata.GetMetadata<IHttpMethodMetadata>(); var httpMethodMetadata = routeEndpoint.Metadata.GetMetadata<IHttpMethodMetadata>();
if (httpMethodMetadata != null) if (httpMethodMetadata != null)
{ {
sb.Append(", Http Methods: "); sb.Append(", Http Methods: ");
sb.Append(string.Join(", ", httpMethodMetadata.HttpMethods)); sb.Append(string.Join(", ", httpMethodMetadata.HttpMethods));
} }
sb.Append(", Display Name: "); sb.Append(", Display Name: ");
sb.Append(matcherEndpoint.DisplayName); sb.Append(routeEndpoint.DisplayName);
sb.AppendLine(); sb.AppendLine();
} }
else else
{ {
sb.Append("Non-MatcherEndpoint. DisplayName:"); sb.Append("Non-RouteEndpoint. DisplayName:");
sb.AppendLine(endpoint.DisplayName); sb.AppendLine(endpoint.DisplayName);
} }
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing
{ {

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing
{ {

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;

View File

@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Routing
{ {
public DefaultLinkGenerationTemplate( public DefaultLinkGenerationTemplate(
DefaultLinkGenerator linkGenerator, DefaultLinkGenerator linkGenerator,
IEnumerable<MatcherEndpoint> endpoints, IEnumerable<RouteEndpoint> endpoints,
HttpContext httpContext, HttpContext httpContext,
RouteValueDictionary explicitValues, RouteValueDictionary explicitValues,
RouteValueDictionary ambientValues) RouteValueDictionary ambientValues)
@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Routing
internal DefaultLinkGenerator LinkGenerator { get; } internal DefaultLinkGenerator LinkGenerator { get; }
internal IEnumerable<MatcherEndpoint> Endpoints { get; } internal IEnumerable<RouteEndpoint> Endpoints { get; }
internal HttpContext HttpContext { get; } internal HttpContext HttpContext { get; }

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing.Internal; using Microsoft.AspNetCore.Routing.Internal;
using Microsoft.AspNetCore.Routing.Matching; using Microsoft.AspNetCore.Routing.Matching;
using Microsoft.AspNetCore.Routing.Template; using Microsoft.AspNetCore.Routing.Template;
@ -97,7 +98,7 @@ namespace Microsoft.AspNetCore.Routing
internal string MakeLink( internal string MakeLink(
HttpContext httpContext, HttpContext httpContext,
MatcherEndpoint endpoint, RouteEndpoint endpoint,
RouteValueDictionary ambientValues, RouteValueDictionary ambientValues,
RouteValueDictionary explicitValues, RouteValueDictionary explicitValues,
LinkOptions options) LinkOptions options)
@ -232,7 +233,7 @@ namespace Microsoft.AspNetCore.Routing
private bool MatchesConstraints( private bool MatchesConstraints(
HttpContext httpContext, HttpContext httpContext,
MatcherEndpoint endpoint, RouteEndpoint endpoint,
RouteValueDictionary routeValues) RouteValueDictionary routeValues)
{ {
if (routeValues == null) if (routeValues == null)
@ -303,16 +304,16 @@ namespace Microsoft.AspNetCore.Routing
{ {
if (httpContext != null) if (httpContext != null)
{ {
var feature = httpContext.Features.Get<IEndpointFeature>(); var feature = httpContext.Features.Get<IRouteValuesFeature>();
if (feature != null) if (feature != null)
{ {
return feature.Values; return feature.RouteValues;
} }
} }
return new RouteValueDictionary(); return new RouteValueDictionary();
} }
private IEnumerable<MatcherEndpoint> FindEndpoints<TAddress>(TAddress address) private IEnumerable<RouteEndpoint> FindEndpoints<TAddress>(TAddress address)
{ {
var finder = _serviceProvider.GetRequiredService<IEndpointFinder<TAddress>>(); var finder = _serviceProvider.GetRequiredService<IEndpointFinder<TAddress>>();
var endpoints = finder.FindEndpoints(address); var endpoints = finder.FindEndpoints(address);
@ -321,13 +322,13 @@ namespace Microsoft.AspNetCore.Routing
return null; return null;
} }
var matcherEndpoints = endpoints.OfType<MatcherEndpoint>(); var routeEndpoints = endpoints.OfType<RouteEndpoint>();
if (!matcherEndpoints.Any()) if (!routeEndpoints.Any())
{ {
return null; return null;
} }
return matcherEndpoints; return routeEndpoints;
} }
} }
} }

View File

@ -2,6 +2,7 @@
// 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.Collections.Generic;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing

View File

@ -3,39 +3,31 @@
using System; using System;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing
{ {
/// <summary> public sealed class EndpointFeature : IEndpointFeature, IRouteValuesFeature, IRoutingFeature
/// A default implementation of <see cref="IEndpointFeature"/> and <see cref="IRoutingFeature"/>.
/// </summary>
public sealed class EndpointFeature : IEndpointFeature, IRoutingFeature
{ {
private RouteData _routeData; private RouteData _routeData;
private RouteValueDictionary _values; private RouteValueDictionary _routeValues;
/// <summary> /// <summary>
/// Gets or sets the selected <see cref="Routing.Endpoint"/> for the current /// Gets or sets the selected <see cref="Http.Endpoint"/> for the current
/// request. /// request.
/// </summary> /// </summary>
public Endpoint Endpoint { get; set; } public Endpoint Endpoint { get; set; }
/// <summary>
/// Gets or sets a delegate that can be used to invoke the current
/// <see cref="Routing.Endpoint"/>.
/// </summary>
public Func<RequestDelegate, RequestDelegate> Invoker { get; set; }
/// <summary> /// <summary>
/// Gets or sets the <see cref="RouteValueDictionary"/> associated with the currrent /// Gets or sets the <see cref="RouteValueDictionary"/> associated with the currrent
/// request. /// request.
/// </summary> /// </summary>
public RouteValueDictionary Values public RouteValueDictionary RouteValues
{ {
get => _values; get => _routeValues;
set set
{ {
_values = value; _routeValues = value;
// RouteData will be created next get with new Values // RouteData will be created next get with new Values
_routeData = null; _routeData = null;
@ -46,7 +38,7 @@ namespace Microsoft.AspNetCore.Routing
/// Gets or sets the <see cref="RouteData"/> for the current request. /// Gets or sets the <see cref="RouteData"/> for the current request.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// The setter is not implemented. Use <see cref="Values"/> to set the route values. /// The setter is not implemented. Use <see cref="RouteValues"/> to set the route values.
/// </remarks> /// </remarks>
RouteData IRoutingFeature.RouteData RouteData IRoutingFeature.RouteData
{ {
@ -54,7 +46,7 @@ namespace Microsoft.AspNetCore.Routing
{ {
if (_routeData == null) if (_routeData == null)
{ {
_routeData = _values == null ? new RouteData() : new RouteData(_values); _routeData = _routeValues == null ? new RouteData() : new RouteData(_routeValues);
// Note: DataTokens won't update if someone else overwrites the Endpoint // Note: DataTokens won't update if someone else overwrites the Endpoint
// after route values has been set. This seems find since endpoints are a new // after route values has been set. This seems find since endpoints are a new
@ -75,4 +67,4 @@ namespace Microsoft.AspNetCore.Routing
set => throw new NotSupportedException(); set => throw new NotSupportedException();
} }
} }
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing
@ -40,13 +41,13 @@ namespace Microsoft.AspNetCore.Routing
throw new InvalidOperationException(message); throw new InvalidOperationException(message);
} }
if (feature.Invoker != null) if (feature.Endpoint?.RequestDelegate != null)
{ {
Log.ExecutingEndpoint(_logger, feature.Endpoint); Log.ExecutingEndpoint(_logger, feature.Endpoint);
try try
{ {
await feature.Invoker(_next)(httpContext); await feature.Endpoint.RequestDelegate(httpContext);
} }
finally finally
{ {

View File

@ -5,6 +5,7 @@ using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing.Matching; using Microsoft.AspNetCore.Routing.Matching;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -54,11 +55,10 @@ namespace Microsoft.AspNetCore.Routing
public async Task Invoke(HttpContext httpContext) public async Task Invoke(HttpContext httpContext)
{ {
// For back-compat EndpointRouteValuesFeature implements IEndpointFeature, IRouteValuesFeature and IRoutingFeature
var feature = new EndpointFeature(); var feature = new EndpointFeature();
httpContext.Features.Set<IEndpointFeature>(feature);
// Back compat support for users of IRoutingFeature SetEndpointFeature(httpContext, feature);
httpContext.Features.Set<IRoutingFeature>(feature);
// There's an inherent race condition between waiting for init and accessing the matcher // There's an inherent race condition between waiting for init and accessing the matcher
// this is OK because once `_matcher` is initialized, it will not be set to null again. // this is OK because once `_matcher` is initialized, it will not be set to null again.
@ -77,6 +77,18 @@ namespace Microsoft.AspNetCore.Routing
await _next(httpContext); await _next(httpContext);
} }
private static void SetEndpointFeature(HttpContext httpContext, EndpointFeature feature)
{
// An IRouteValuesFeature might have already been set
// Copy its RouteValues collection if present
var currentRouteValuesFeature = httpContext.Features.Get<IRouteValuesFeature>();
feature.RouteValues = currentRouteValuesFeature?.RouteValues;
httpContext.Features.Set<IRoutingFeature>(feature);
httpContext.Features.Set<IRouteValuesFeature>(feature);
httpContext.Features.Set<IEndpointFeature>(feature);
}
// Initialization is async to avoid blocking threads while reflection and things // Initialization is async to avoid blocking threads while reflection and things
// of that nature take place. // of that nature take place.
// //
@ -127,4 +139,4 @@ namespace Microsoft.AspNetCore.Routing
} }
} }
} }
} }

View File

@ -2,6 +2,7 @@
// 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.Collections.Generic;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing
{ {

View File

@ -2,6 +2,7 @@
// 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.Collections.Generic;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing
{ {

View File

@ -38,8 +38,7 @@ namespace Microsoft.AspNetCore.Routing.Internal
var endpoints = dataSource.Endpoints; var endpoints = dataSource.Endpoints;
for (var i = 0; i < endpoints.Count; i++) for (var i = 0; i < endpoints.Count; i++)
{ {
var endpoint = endpoints[i] as MatcherEndpoint; if (endpoints[i] is RouteEndpoint endpoint && endpoint.Metadata.GetMetadata<ISuppressMatchingMetadata>() == null)
if (endpoint != null && endpoint.Metadata.GetMetadata<ISuppressMatchingMetadata>() == null)
{ {
builder.AddEndpoint(endpoint); builder.AddEndpoint(endpoint);
} }

View File

@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
{ {
internal readonly struct Candidate internal readonly struct Candidate
{ {
public readonly MatcherEndpoint Endpoint; public readonly RouteEndpoint Endpoint;
// Used to optimize out operations that modify route values. // Used to optimize out operations that modify route values.
public readonly CandidateFlags Flags; public readonly CandidateFlags Flags;
@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
public readonly int Score; public readonly int Score;
// Used in tests. // Used in tests.
public Candidate(MatcherEndpoint endpoint) public Candidate(RouteEndpoint endpoint)
{ {
Endpoint = endpoint; Endpoint = endpoint;
@ -63,7 +63,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
} }
public Candidate( public Candidate(
MatcherEndpoint endpoint, RouteEndpoint endpoint,
int score, int score,
KeyValuePair<string, object>[] slots, KeyValuePair<string, object>[] slots,
(string parameterName, int segmentIndex, int slotIndex)[] captures, (string parameterName, int segmentIndex, int slotIndex)[] captures,

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
{ {
@ -35,7 +36,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
/// </summary> /// </summary>
/// <param name="endpoints">The list of endpoints, sorted in descending priority order.</param> /// <param name="endpoints">The list of endpoints, sorted in descending priority order.</param>
/// <param name="scores">The list of endpoint scores. <see cref="CandidateState.Score"/>.</param> /// <param name="scores">The list of endpoint scores. <see cref="CandidateState.Score"/>.</param>
public CandidateSet(MatcherEndpoint[] endpoints, int[] scores) public CandidateSet(RouteEndpoint[] endpoints, int[] scores)
{ {
Count = endpoints.Length; Count = endpoints.Length;

View File

@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
/// </summary> /// </summary>
public struct CandidateState public struct CandidateState
{ {
internal CandidateState(MatcherEndpoint endpoint, int score) internal CandidateState(RouteEndpoint endpoint, int score)
{ {
Endpoint = endpoint; Endpoint = endpoint;
Score = score; Score = score;
@ -18,12 +18,12 @@ namespace Microsoft.AspNetCore.Routing.Matching
} }
/// <summary> /// <summary>
/// Gets the <see cref="Routing.Endpoint"/>. /// Gets the <see cref="Http.Endpoint"/>.
/// </summary> /// </summary>
public MatcherEndpoint Endpoint { get; } public RouteEndpoint Endpoint { get; }
/// <summary> /// <summary>
/// Gets the score of the <see cref="Routing.Endpoint"/> within the current /// Gets the score of the <see cref="Http.Endpoint"/> within the current
/// <see cref="CandidateSet"/>. /// <see cref="CandidateSet"/>.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
@ -40,15 +40,15 @@ namespace Microsoft.AspNetCore.Routing.Matching
public int Score { get; } public int Score { get; }
/// <summary> /// <summary>
/// Gets or sets a value which indicates where the <see cref="Routing.Endpoint"/> is considered /// Gets or sets a value which indicates where the <see cref="Http.Endpoint"/> is considered
/// a valid candiate for the current request. Set this value to <c>false</c> to exclude an /// a valid candiate for the current request. Set this value to <c>false</c> to exclude an
/// <see cref="Routing.Endpoint"/> from consideration. /// <see cref="Http.Endpoint"/> from consideration.
/// </summary> /// </summary>
public bool IsValidCandidate { get; set; } public bool IsValidCandidate { get; set; }
/// <summary> /// <summary>
/// Gets or sets the <see cref="RouteValueDictionary"/> associated with the /// Gets or sets the <see cref="RouteValueDictionary"/> associated with the
/// <see cref="Routing.Endpoint"/> and the current request. /// <see cref="Http.Endpoint"/> and the current request.
/// </summary> /// </summary>
public RouteValueDictionary Values { get; set; } public RouteValueDictionary Values { get; set; }
} }

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
{ {
@ -26,7 +27,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
// Used in tests // Used in tests
internal Matcher CurrentMatcher => _cache.Value; internal Matcher CurrentMatcher => _cache.Value;
public override Task MatchAsync(HttpContext httpContext, IEndpointFeature feature) public override Task MatchAsync(HttpContext httpContext, EndpointFeature feature)
{ {
return CurrentMatcher.MatchAsync(httpContext, feature); return CurrentMatcher.MatchAsync(httpContext, feature);
} }
@ -36,11 +37,10 @@ namespace Microsoft.AspNetCore.Routing.Matching
var builder = _matcherBuilderFactory(); var builder = _matcherBuilderFactory();
for (var i = 0; i < endpoints.Count; i++) for (var i = 0; i < endpoints.Count; i++)
{ {
// By design we only look at MatcherEndpoint here. It's possible to // By design we only look at RouteEndpoint here. It's possible to
// register other endpoint types, which are non-routable, and it's // register other endpoint types, which are non-routable, and it's
// ok that we won't route to them. // ok that we won't route to them.
var endpoint = endpoints[i] as MatcherEndpoint; if (endpoints[i] is RouteEndpoint endpoint && endpoint.Metadata.GetMetadata<ISuppressMatchingMetadata>() == null)
if (endpoint != null && endpoint.Metadata.GetMetadata<ISuppressMatchingMetadata>() == null)
{ {
builder.AddEndpoint(endpoint); builder.AddEndpoint(endpoint);
} }

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
{ {
@ -25,7 +26,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
public override Task SelectAsync( public override Task SelectAsync(
HttpContext httpContext, HttpContext httpContext,
IEndpointFeature feature, EndpointFeature feature,
CandidateSet candidateSet) CandidateSet candidateSet)
{ {
for (var i = 0; i < _selectorPolicies.Length; i++) for (var i = 0; i < _selectorPolicies.Length; i++)
@ -33,7 +34,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
_selectorPolicies[i].Apply(httpContext, candidateSet); _selectorPolicies[i].Apply(httpContext, candidateSet);
} }
MatcherEndpoint endpoint = null; RouteEndpoint endpoint = null;
RouteValueDictionary values = null; RouteValueDictionary values = null;
int? foundScore = null; int? foundScore = null;
for (var i = 0; i < candidateSet.Count; i++) for (var i = 0; i < candidateSet.Count; i++)
@ -73,8 +74,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
if (endpoint != null) if (endpoint != null)
{ {
feature.Endpoint = endpoint; feature.Endpoint = endpoint;
feature.Invoker = endpoint.Invoker; feature.RouteValues = values;
feature.Values = values;
} }
return Task.CompletedTask; return Task.CompletedTask;
@ -84,7 +84,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
{ {
// If we get here it's the result of an ambiguity - we're OK with this // If we get here it's the result of an ambiguity - we're OK with this
// being a littler slower and more allocatey. // being a littler slower and more allocatey.
var matches = new List<MatcherEndpoint>(); var matches = new List<RouteEndpoint>();
for (var i = 0; i < candidates.Count; i++) for (var i = 0; i < candidates.Count; i++)
{ {
ref var state = ref candidates[i]; ref var state = ref candidates[i];

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Patterns;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
@ -22,7 +23,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
_maxSegmentCount = maxSegmentCount; _maxSegmentCount = maxSegmentCount;
} }
public sealed override Task MatchAsync(HttpContext httpContext, IEndpointFeature feature) public sealed override Task MatchAsync(HttpContext httpContext, EndpointFeature feature)
{ {
if (httpContext == null) if (httpContext == null)
{ {

View File

@ -10,13 +10,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
{ {
internal class DfaMatcherBuilder : MatcherBuilder internal class DfaMatcherBuilder : MatcherBuilder
{ {
private readonly List<MatcherEndpoint> _endpoints = new List<MatcherEndpoint>(); private readonly List<RouteEndpoint> _endpoints = new List<RouteEndpoint>();
private readonly ParameterPolicyFactory _parameterPolicyFactory; private readonly ParameterPolicyFactory _parameterPolicyFactory;
private readonly EndpointSelector _selector; private readonly EndpointSelector _selector;
private readonly MatcherPolicy[] _policies; private readonly MatcherPolicy[] _policies;
private readonly INodeBuilderPolicy[] _nodeBuilders; private readonly INodeBuilderPolicy[] _nodeBuilders;
private readonly MatcherEndpointComparer _comparer; private readonly RouteEndpointComparer _comparer;
public DfaMatcherBuilder( public DfaMatcherBuilder(
ParameterPolicyFactory parameterPolicyFactory, ParameterPolicyFactory parameterPolicyFactory,
@ -29,10 +29,10 @@ namespace Microsoft.AspNetCore.Routing.Matching
// Taking care to use _policies, which has been sorted. // Taking care to use _policies, which has been sorted.
_nodeBuilders = _policies.OfType<INodeBuilderPolicy>().ToArray(); _nodeBuilders = _policies.OfType<INodeBuilderPolicy>().ToArray();
_comparer = new MatcherEndpointComparer(_policies.OfType<IEndpointComparerPolicy>().ToArray()); _comparer = new RouteEndpointComparer(_policies.OfType<IEndpointComparerPolicy>().ToArray());
} }
public override void AddEndpoint(MatcherEndpoint endpoint) public override void AddEndpoint(RouteEndpoint endpoint)
{ {
_endpoints.Add(endpoint); _endpoints.Add(endpoint);
} }
@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
// Since we're doing a BFS we will process each 'level' of the tree in stages // Since we're doing a BFS we will process each 'level' of the tree in stages
// this list will hold the set of items we need to process at the current // this list will hold the set of items we need to process at the current
// stage. // stage.
var work = new List<(MatcherEndpoint endpoint, List<DfaNode> parents)>(); var work = new List<(RouteEndpoint endpoint, List<DfaNode> parents)>();
var root = new DfaNode() { PathDepth = 0, Label = "/" }; var root = new DfaNode() { PathDepth = 0, Label = "/" };
@ -67,7 +67,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
for (var depth = 0; depth <= maxDepth; depth++) for (var depth = 0; depth <= maxDepth; depth++)
{ {
// As we process items, collect the next set of items. // As we process items, collect the next set of items.
var nextWork = new List<(MatcherEndpoint endpoint, List<DfaNode> parents)>(); var nextWork = new List<(RouteEndpoint endpoint, List<DfaNode> parents)>();
for (var i = 0; i < work.Count; i++) for (var i = 0; i < work.Count; i++)
{ {
@ -192,7 +192,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
return root; return root;
} }
private RoutePatternPathSegment GetCurrentSegment(MatcherEndpoint endpoint, int depth) private RoutePatternPathSegment GetCurrentSegment(RouteEndpoint endpoint, int depth)
{ {
if (depth < endpoint.RoutePattern.PathSegments.Count) if (depth < endpoint.RoutePattern.PathSegments.Count)
{ {
@ -337,7 +337,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
// Builds an array of candidates for a node, assigns a 'score' for each // Builds an array of candidates for a node, assigns a 'score' for each
// endpoint. // endpoint.
internal Candidate[] CreateCandidates(IReadOnlyList<MatcherEndpoint> endpoints) internal Candidate[] CreateCandidates(IReadOnlyList<RouteEndpoint> endpoints)
{ {
if (endpoints.Count == 0) if (endpoints.Count == 0)
{ {
@ -367,7 +367,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
} }
// internal for tests // internal for tests
internal Candidate CreateCandidate(MatcherEndpoint endpoint, int score) internal Candidate CreateCandidate(RouteEndpoint endpoint, int score)
{ {
var assignments = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); var assignments = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
var slots = new List<KeyValuePair<string, object>>(); var slots = new List<KeyValuePair<string, object>>();
@ -481,7 +481,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
return groups.ToArray(); return groups.ToArray();
} }
private static bool HasAdditionalRequiredSegments(MatcherEndpoint endpoint, int depth) private static bool HasAdditionalRequiredSegments(RouteEndpoint endpoint, int depth)
{ {
for (var i = depth; i < endpoint.RoutePattern.PathSegments.Count; i++) for (var i = depth; i < endpoint.RoutePattern.PathSegments.Count; i++)
{ {
@ -550,7 +550,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
}; };
// TODO: https://github.com/aspnet/Routing/issues/648 // TODO: https://github.com/aspnet/Routing/issues/648
next.Matches.AddRange(edge.Endpoints.Cast<MatcherEndpoint>().ToArray()); next.Matches.AddRange(edge.Endpoints.Cast<RouteEndpoint>().ToArray());
nextWork.Add(next); nextWork.Add(next);
parent.PolicyEdges.Add(edge.State, next); parent.PolicyEdges.Add(edge.State, next);

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
public DfaNode() public DfaNode()
{ {
Literals = new Dictionary<string, DfaNode>(StringComparer.OrdinalIgnoreCase); Literals = new Dictionary<string, DfaNode>(StringComparer.OrdinalIgnoreCase);
Matches = new List<MatcherEndpoint>(); Matches = new List<RouteEndpoint>();
PolicyEdges = new Dictionary<object, DfaNode>(); PolicyEdges = new Dictionary<object, DfaNode>();
} }
@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
// Just for diagnostics and debugging // Just for diagnostics and debugging
public string Label { get; set; } public string Label { get; set; }
public List<MatcherEndpoint> Matches { get; } public List<RouteEndpoint> Matches { get; }
public Dictionary<string, DfaNode> Literals { get; } public Dictionary<string, DfaNode> Literals { get; }

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
{ {

View File

@ -3,6 +3,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
{ {
@ -21,13 +22,12 @@ namespace Microsoft.AspNetCore.Routing.Matching
/// <param name="candidates">The <see cref="CandidateSet"/>.</param> /// <param name="candidates">The <see cref="CandidateSet"/>.</param>
/// <returns>A <see cref="Task"/> that completes asynchronously once endpoint selection is complete.</returns> /// <returns>A <see cref="Task"/> that completes asynchronously once endpoint selection is complete.</returns>
/// <remarks> /// <remarks>
/// An <see cref="EndpointSelector"/> should assign the <see cref="IEndpointFeature.Endpoint"/>, /// An <see cref="EndpointSelector"/> should assign the <see cref="EndpointFeature.Endpoint"/>
/// <see cref="IEndpointFeature.Invoker"/>, and <see cref="IEndpointFeature.Values"/> properties /// and <see cref="EndpointFeature.RouteValues"/> properties once an endpoint is selected.
/// once an endpoint is selected.
/// </remarks> /// </remarks>
public abstract Task SelectAsync( public abstract Task SelectAsync(
HttpContext httpContext, HttpContext httpContext,
IEndpointFeature feature, EndpointFeature feature,
CandidateSet candidates); CandidateSet candidates);
} }
} }

View File

@ -250,8 +250,8 @@ namespace Microsoft.AspNetCore.Routing.Matching
private Endpoint CreateRejectionEndpoint(IEnumerable<string> httpMethods) private Endpoint CreateRejectionEndpoint(IEnumerable<string> httpMethods)
{ {
var allow = string.Join(", ", httpMethods); var allow = string.Join(", ", httpMethods);
return new MatcherEndpoint( return new RouteEndpoint(
(next) => (context) => (context) =>
{ {
context.Response.StatusCode = 405; context.Response.StatusCode = 405;
context.Response.Headers.Add("Allow", allow); context.Response.Headers.Add("Allow", allow);

View File

@ -2,6 +2,7 @@
// 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.Collections.Generic;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
{ {

View File

@ -2,6 +2,7 @@
// 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.Collections.Generic;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
{ {

View File

@ -3,6 +3,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
{ {
@ -18,8 +19,8 @@ namespace Microsoft.AspNetCore.Routing.Matching
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param> /// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
/// <param name="feature"> /// <param name="feature">
/// The <see cref="IEndpointFeature"/> associated with the current request. The /// The <see cref="IEndpointFeature"/> associated with the current request. The
/// <see cref="IEndpointFeature"/> will be mutated to contain the result of the operation.</param> /// <see cref="EndpointFeature"/> will be mutated to contain the result of the operation.</param>
/// <returns>A <see cref="Task"/> which represents the asynchronous completion of the operation.</returns> /// <returns>A <see cref="Task"/> which represents the asynchronous completion of the operation.</returns>
public abstract Task MatchAsync(HttpContext httpContext, IEndpointFeature feature); public abstract Task MatchAsync(HttpContext httpContext, EndpointFeature feature);
} }
} }

View File

@ -5,7 +5,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
{ {
internal abstract class MatcherBuilder internal abstract class MatcherBuilder
{ {
public abstract void AddEndpoint(MatcherEndpoint endpoint); public abstract void AddEndpoint(RouteEndpoint endpoint);
public abstract Matcher Build(); public abstract Matcher Build();
} }

View File

@ -2,6 +2,7 @@
// 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.Collections.Generic;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
{ {

View File

@ -6,7 +6,7 @@ using System.Diagnostics;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
{ {
// Use to sort and group MatcherEndpoints. // Use to sort and group RouteEndpoints.
// //
// NOTE: // NOTE:
// When ordering endpoints, we compare the route templates as an absolute last resort. // When ordering endpoints, we compare the route templates as an absolute last resort.
@ -20,14 +20,14 @@ namespace Microsoft.AspNetCore.Routing.Matching
// IComparer implementation considers the template string as a tiebreaker. // IComparer implementation considers the template string as a tiebreaker.
// IEqualityComparer implementation does not. // IEqualityComparer implementation does not.
// This is cool and good. // This is cool and good.
internal class MatcherEndpointComparer : IComparer<MatcherEndpoint>, IEqualityComparer<MatcherEndpoint> internal class RouteEndpointComparer : IComparer<RouteEndpoint>, IEqualityComparer<RouteEndpoint>
{ {
private readonly IComparer<MatcherEndpoint>[] _comparers; private readonly IComparer<RouteEndpoint>[] _comparers;
public MatcherEndpointComparer(IEndpointComparerPolicy[] policies) public RouteEndpointComparer(IEndpointComparerPolicy[] policies)
{ {
// Order, Precedence, (others)... // Order, Precedence, (others)...
_comparers = new IComparer<MatcherEndpoint>[2 + policies.Length]; _comparers = new IComparer<RouteEndpoint>[2 + policies.Length];
_comparers[0] = OrderComparer.Instance; _comparers[0] = OrderComparer.Instance;
_comparers[1] = PrecedenceComparer.Instance; _comparers[1] = PrecedenceComparer.Instance;
for (var i = 0; i < policies.Length; i++) for (var i = 0; i < policies.Length; i++)
@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
} }
} }
public int Compare(MatcherEndpoint x, MatcherEndpoint y) public int Compare(RouteEndpoint x, RouteEndpoint y)
{ {
// We don't expose this publicly, and we should never call it on // We don't expose this publicly, and we should never call it on
// a null endpoint. // a null endpoint.
@ -49,7 +49,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
return compare == 0 ? x.RoutePattern.RawText.CompareTo(y.RoutePattern.RawText) : compare; return compare == 0 ? x.RoutePattern.RawText.CompareTo(y.RoutePattern.RawText) : compare;
} }
public bool Equals(MatcherEndpoint x, MatcherEndpoint y) public bool Equals(RouteEndpoint x, RouteEndpoint y)
{ {
// We don't expose this publicly, and we should never call it on // We don't expose this publicly, and we should never call it on
// a null endpoint. // a null endpoint.
@ -59,14 +59,14 @@ namespace Microsoft.AspNetCore.Routing.Matching
return CompareCore(x, y) == 0; return CompareCore(x, y) == 0;
} }
public int GetHashCode(MatcherEndpoint obj) public int GetHashCode(RouteEndpoint obj)
{ {
// This should not be possible to call publicly. // This should not be possible to call publicly.
Debug.Fail("We don't expect this to be called."); Debug.Fail("We don't expect this to be called.");
throw new System.NotImplementedException(); throw new System.NotImplementedException();
} }
private int CompareCore(MatcherEndpoint x, MatcherEndpoint y) private int CompareCore(RouteEndpoint x, RouteEndpoint y)
{ {
for (var i = 0; i < _comparers.Length; i++) for (var i = 0; i < _comparers.Length; i++)
{ {
@ -80,21 +80,21 @@ namespace Microsoft.AspNetCore.Routing.Matching
return 0; return 0;
} }
private class OrderComparer : IComparer<MatcherEndpoint> private class OrderComparer : IComparer<RouteEndpoint>
{ {
public static readonly IComparer<MatcherEndpoint> Instance = new OrderComparer(); public static readonly IComparer<RouteEndpoint> Instance = new OrderComparer();
public int Compare(MatcherEndpoint x, MatcherEndpoint y) public int Compare(RouteEndpoint x, RouteEndpoint y)
{ {
return x.Order.CompareTo(y.Order); return x.Order.CompareTo(y.Order);
} }
} }
private class PrecedenceComparer : IComparer<MatcherEndpoint> private class PrecedenceComparer : IComparer<RouteEndpoint>
{ {
public static readonly IComparer<MatcherEndpoint> Instance = new PrecedenceComparer(); public static readonly IComparer<RouteEndpoint> Instance = new PrecedenceComparer();
public int Compare(MatcherEndpoint x, MatcherEndpoint y) public int Compare(RouteEndpoint x, RouteEndpoint y)
{ {
return x.RoutePattern.InboundPrecedence.CompareTo(y.RoutePattern.InboundPrecedence); return x.RoutePattern.InboundPrecedence.CompareTo(y.RoutePattern.InboundPrecedence);
} }

View File

@ -7,39 +7,34 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Patterns;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing
{ {
/// <summary> /// <summary>
/// Represents an <see cref="Endpoint"/> that can be used in URL matching or URL generation. /// Represents an <see cref="Endpoint"/> that can be used in URL matching or URL generation.
/// </summary> /// </summary>
public sealed class MatcherEndpoint : Endpoint public sealed class RouteEndpoint : Endpoint
{ {
internal static readonly Func<RequestDelegate, RequestDelegate> EmptyInvoker = (next) =>
{
return (context) => Task.CompletedTask;
};
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="MatcherEndpoint"/> class. /// Initializes a new instance of the <see cref="RouteEndpoint"/> class.
/// </summary> /// </summary>
/// <param name="invoker">The delegate to invoke to create a <see cref="RequestDelegate"/>.</param> /// <param name="requestDelegate">The delegate used to process requests for the endpoint.</param>
/// <param name="routePattern">The <see cref="RoutePattern"/> to use in URL matching.</param> /// <param name="routePattern">The <see cref="RoutePattern"/> to use in URL matching.</param>
/// <param name="order">The order assigned to the endpoint.</param> /// <param name="order">The order assigned to the endpoint.</param>
/// <param name="metadata"> /// <param name="metadata">
/// The <see cref="EndpointMetadataCollection"/> or metadata associated with the endpoint. /// The <see cref="EndpointMetadataCollection"/> or metadata associated with the endpoint.
/// </param> /// </param>
/// <param name="displayName">The informational display name of the endpoint.</param> /// <param name="displayName">The informational display name of the endpoint.</param>
public MatcherEndpoint( public RouteEndpoint(
Func<RequestDelegate, RequestDelegate> invoker, RequestDelegate requestDelegate,
RoutePattern routePattern, RoutePattern routePattern,
int order, int order,
EndpointMetadataCollection metadata, EndpointMetadataCollection metadata,
string displayName) string displayName)
: base(metadata, displayName) : base(requestDelegate, metadata, displayName)
{ {
if (invoker == null) if (requestDelegate == null)
{ {
throw new ArgumentNullException(nameof(invoker)); throw new ArgumentNullException(nameof(requestDelegate));
} }
if (routePattern == null) if (routePattern == null)
@ -47,16 +42,10 @@ namespace Microsoft.AspNetCore.Routing.Matching
throw new ArgumentNullException(nameof(routePattern)); throw new ArgumentNullException(nameof(routePattern));
} }
Invoker = invoker;
RoutePattern = routePattern; RoutePattern = routePattern;
Order = order; Order = order;
} }
/// <summary>
/// Gets the invoker. The invoker is a delegate used to create a <see cref="RequestDelegate"/>.
/// </summary>
public Func<RequestDelegate, RequestDelegate> Invoker { get; }
/// <summary> /// <summary>
/// Gets the order value of endpoint. /// Gets the order value of endpoint.
/// </summary> /// </summary>

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Internal; using Microsoft.AspNetCore.Routing.Internal;
using Microsoft.AspNetCore.Routing.Matching; using Microsoft.AspNetCore.Routing.Matching;
using Microsoft.AspNetCore.Routing.Template; using Microsoft.AspNetCore.Routing.Template;
@ -56,7 +57,7 @@ namespace Microsoft.AspNetCore.Routing
return matchResults return matchResults
.Select(matchResult => matchResult.Match) .Select(matchResult => matchResult.Match)
.Select(match => (MatcherEndpoint)match.Entry.Data); .Select(match => (RouteEndpoint)match.Entry.Data);
} }
private void HandleChange() private void HandleChange()
@ -103,7 +104,7 @@ namespace Microsoft.AspNetCore.Routing
var namedOutboundMatchResults = new Dictionary<string, List<OutboundMatchResult>>( var namedOutboundMatchResults = new Dictionary<string, List<OutboundMatchResult>>(
StringComparer.OrdinalIgnoreCase); StringComparer.OrdinalIgnoreCase);
var endpoints = _endpointDataSource.Endpoints.OfType<MatcherEndpoint>(); var endpoints = _endpointDataSource.Endpoints.OfType<RouteEndpoint>();
foreach (var endpoint in endpoints) foreach (var endpoint in endpoints)
{ {
// Do not consider an endpoint for link generation if the following marker metadata is on it // Do not consider an endpoint for link generation if the following marker metadata is on it
@ -135,7 +136,7 @@ namespace Microsoft.AspNetCore.Routing
return (allOutboundMatches, namedOutboundMatchResults); return (allOutboundMatches, namedOutboundMatchResults);
} }
private OutboundRouteEntry CreateOutboundRouteEntry(MatcherEndpoint endpoint) private OutboundRouteEntry CreateOutboundRouteEntry(RouteEndpoint endpoint)
{ {
var routeValuesAddressMetadata = endpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>(); var routeValuesAddressMetadata = endpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
var entry = new OutboundRouteEntry() var entry = new OutboundRouteEntry()

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Microsoft.AspNetCore.Http;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing

View File

@ -5,6 +5,7 @@ using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder.Internal; using Microsoft.AspNetCore.Builder.Internal;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Moq; using Moq;

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Matching; using Microsoft.AspNetCore.Routing.Matching;
using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.AspNetCore.Routing.TestObjects; using Microsoft.AspNetCore.Routing.TestObjects;
@ -146,14 +147,14 @@ namespace Microsoft.AspNetCore.Routing
Assert.False(token.HasChanged); Assert.False(token.HasChanged);
} }
private MatcherEndpoint CreateEndpoint( private RouteEndpoint CreateEndpoint(
string template, string template,
object defaults = null, object defaults = null,
int order = 0, int order = 0,
string routeName = null) string routeName = null)
{ {
return new MatcherEndpoint( return new RouteEndpoint(
MatcherEndpoint.EmptyInvoker, TestConstants.EmptyRequestDelegate,
RoutePatternFactory.Parse(template, defaults, parameterPolicies: null), RoutePatternFactory.Parse(template, defaults, parameterPolicies: null),
order, order,
EndpointMetadataCollection.Empty, EndpointMetadataCollection.Empty,

View File

@ -1,10 +1,10 @@
// 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 Microsoft.AspNetCore.Routing.TestObjects;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Microsoft.AspNetCore.Http;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing
@ -16,8 +16,8 @@ namespace Microsoft.AspNetCore.Routing
{ {
// Arrange & Act // Arrange & Act
var dataSource = new DefaultEndpointDataSource( var dataSource = new DefaultEndpointDataSource(
new TestEndpoint(EndpointMetadataCollection.Empty, "1"), new Endpoint(TestConstants.EmptyRequestDelegate, EndpointMetadataCollection.Empty, "1"),
new TestEndpoint(EndpointMetadataCollection.Empty, "2") new Endpoint(TestConstants.EmptyRequestDelegate, EndpointMetadataCollection.Empty, "2")
); );
// Assert // Assert
@ -32,8 +32,8 @@ namespace Microsoft.AspNetCore.Routing
// Arrange & Act // Arrange & Act
var dataSource = new DefaultEndpointDataSource(new List<Endpoint> var dataSource = new DefaultEndpointDataSource(new List<Endpoint>
{ {
new TestEndpoint(EndpointMetadataCollection.Empty, "1"), new Endpoint(TestConstants.EmptyRequestDelegate, EndpointMetadataCollection.Empty, "1"),
new TestEndpoint(EndpointMetadataCollection.Empty, "2") new Endpoint(TestConstants.EmptyRequestDelegate, EndpointMetadataCollection.Empty, "2")
}); });
// Assert // Assert

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing.Constraints; using Microsoft.AspNetCore.Routing.Constraints;
using Microsoft.AspNetCore.Routing.Internal; using Microsoft.AspNetCore.Routing.Internal;
using Microsoft.AspNetCore.Routing.Matching; using Microsoft.AspNetCore.Routing.Matching;
@ -25,7 +26,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_Success() public void GetLink_Success()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
// Act // Act
@ -40,7 +41,7 @@ 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 = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
// Act & Assert // Act & Assert
@ -53,7 +54,7 @@ namespace Microsoft.AspNetCore.Routing
public void TryGetLink_Fail() public void TryGetLink_Fail()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
// Act // Act
@ -70,9 +71,9 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_MultipleEndpoints_Success() public void GetLink_MultipleEndpoints_Success()
{ {
// Arrange // Arrange
var endpoint1 = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}/{id?}"); var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}");
var endpoint2 = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}");
var endpoint3 = EndpointFactory.CreateMatcherEndpoint("{controller}"); var endpoint3 = EndpointFactory.CreateRouteEndpoint("{controller}");
var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2, endpoint3); var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2, endpoint3);
// Act // Act
@ -86,9 +87,9 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_MultipleEndpoints_Success2() public void GetLink_MultipleEndpoints_Success2()
{ {
// Arrange // Arrange
var endpoint1 = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}/{id}"); var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}");
var endpoint2 = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}");
var endpoint3 = EndpointFactory.CreateMatcherEndpoint("{controller}"); var endpoint3 = EndpointFactory.CreateRouteEndpoint("{controller}");
var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2, endpoint3); var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2, endpoint3);
// Act // Act
@ -102,7 +103,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_EncodesIntermediate_DefaultValues() public void GetLink_EncodesIntermediate_DefaultValues()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{p1}/{p2=a b}/{p3=foo}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{p1}/{p2=a b}/{p3=foo}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
// Act // Act
@ -118,7 +119,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_EncodesValue_OfSingleAsteriskCatchAllParameter(string routeValue, string expected) public void GetLink_EncodesValue_OfSingleAsteriskCatchAllParameter(string routeValue, string expected)
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}/{*path}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{*path}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" }); var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" });
@ -142,7 +143,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_DoesNotEncodeSlashes_OfDoubleAsteriskCatchAllParameter(string routeValue, string expected) public void GetLink_DoesNotEncodeSlashes_OfDoubleAsteriskCatchAllParameter(string routeValue, string expected)
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}/{**path}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{**path}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" }); var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" });
@ -157,7 +158,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_EncodesContentOtherThanSlashes_OfDoubleAsteriskCatchAllParameter() public void GetLink_EncodesContentOtherThanSlashes_OfDoubleAsteriskCatchAllParameter()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}/{**path}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{**path}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" }); var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" });
@ -172,7 +173,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_EncodesValues() public void GetLink_EncodesValues()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" }); var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" });
@ -187,7 +188,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_ForListOfStrings() public void GetLink_ForListOfStrings()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
var context = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" }); var context = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" });
@ -202,7 +203,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_ForListOfInts() public void GetLink_ForListOfInts()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" }); var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" });
@ -217,7 +218,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_ForList_Empty() public void GetLink_ForList_Empty()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" }); var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" });
@ -232,7 +233,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_ForList_StringWorkaround() public void GetLink_ForList_StringWorkaround()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" }); var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" });
@ -249,7 +250,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_Success_AmbientValues() public void GetLink_Success_AmbientValues()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
var httpContext = CreateHttpContext(ambientValues: new { controller = "Home" }); var httpContext = CreateHttpContext(ambientValues: new { controller = "Home" });
@ -264,7 +265,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_GeneratesLowercaseUrl_SetOnRouteOptions() public void GetLink_GeneratesLowercaseUrl_SetOnRouteOptions()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator(new[] { endpoint }, new RouteOptions() { LowercaseUrls = true }); var linkGenerator = CreateLinkGenerator(new[] { endpoint }, new RouteOptions() { LowercaseUrls = true });
var httpContext = CreateHttpContext(ambientValues: new { controller = "Home" }); var httpContext = CreateHttpContext(ambientValues: new { controller = "Home" });
@ -279,7 +280,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_GeneratesLowercaseQueryString_SetOnRouteOptions() public void GetLink_GeneratesLowercaseQueryString_SetOnRouteOptions()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator( var linkGenerator = CreateLinkGenerator(
new[] { endpoint }, new[] { endpoint },
new RouteOptions() { LowercaseUrls = true, LowercaseQueryStrings = true }); new RouteOptions() { LowercaseUrls = true, LowercaseQueryStrings = true });
@ -298,7 +299,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_GeneratesLowercaseQueryString_OnlyIfLowercaseUrlIsTrue_SetOnRouteOptions() public void GetLink_GeneratesLowercaseQueryString_OnlyIfLowercaseUrlIsTrue_SetOnRouteOptions()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator( var linkGenerator = CreateLinkGenerator(
new[] { endpoint }, new[] { endpoint },
new RouteOptions() { LowercaseUrls = false, LowercaseQueryStrings = true }); new RouteOptions() { LowercaseUrls = false, LowercaseQueryStrings = true });
@ -317,7 +318,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_AppendsTrailingSlash_SetOnRouteOptions() public void GetLink_AppendsTrailingSlash_SetOnRouteOptions()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator( var linkGenerator = CreateLinkGenerator(
new[] { endpoint }, new[] { endpoint },
new RouteOptions() { AppendTrailingSlash = true }); new RouteOptions() { AppendTrailingSlash = true });
@ -334,7 +335,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_GeneratesLowercaseQueryStringAndTrailingSlash_SetOnRouteOptions() public void GetLink_GeneratesLowercaseQueryStringAndTrailingSlash_SetOnRouteOptions()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator( var linkGenerator = CreateLinkGenerator(
new[] { endpoint }, new[] { endpoint },
new RouteOptions() { LowercaseUrls = true, LowercaseQueryStrings = true, AppendTrailingSlash = true }); new RouteOptions() { LowercaseUrls = true, LowercaseQueryStrings = true, AppendTrailingSlash = true });
@ -353,7 +354,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_LowercaseUrlSetToTrue_OnRouteOptions_OverridenByCallsiteValue() public void GetLink_LowercaseUrlSetToTrue_OnRouteOptions_OverridenByCallsiteValue()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator( var linkGenerator = CreateLinkGenerator(
new[] { endpoint }, new[] { endpoint },
new RouteOptions() { LowercaseUrls = true }); new RouteOptions() { LowercaseUrls = true });
@ -376,7 +377,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_LowercaseUrlSetToFalse_OnRouteOptions_OverridenByCallsiteValue() public void GetLink_LowercaseUrlSetToFalse_OnRouteOptions_OverridenByCallsiteValue()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator( var linkGenerator = CreateLinkGenerator(
new[] { endpoint }, new[] { endpoint },
new RouteOptions() { LowercaseUrls = false }); new RouteOptions() { LowercaseUrls = false });
@ -399,7 +400,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_LowercaseUrlQueryStringsSetToTrue_OnRouteOptions_OverridenByCallsiteValue() public void GetLink_LowercaseUrlQueryStringsSetToTrue_OnRouteOptions_OverridenByCallsiteValue()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator( var linkGenerator = CreateLinkGenerator(
new[] { endpoint }, new[] { endpoint },
new RouteOptions() { LowercaseUrls = true, LowercaseQueryStrings = true }); new RouteOptions() { LowercaseUrls = true, LowercaseQueryStrings = true });
@ -423,7 +424,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_LowercaseUrlQueryStringsSetToFalse_OnRouteOptions_OverridenByCallsiteValue() public void GetLink_LowercaseUrlQueryStringsSetToFalse_OnRouteOptions_OverridenByCallsiteValue()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator( var linkGenerator = CreateLinkGenerator(
new[] { endpoint }, new[] { endpoint },
new RouteOptions() { LowercaseUrls = false, LowercaseQueryStrings = false }); new RouteOptions() { LowercaseUrls = false, LowercaseQueryStrings = false });
@ -447,7 +448,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_AppendTrailingSlashSetToFalse_OnRouteOptions_OverridenByCallsiteValue() public void GetLink_AppendTrailingSlashSetToFalse_OnRouteOptions_OverridenByCallsiteValue()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}");
var linkGenerator = CreateLinkGenerator( var linkGenerator = CreateLinkGenerator(
new[] { endpoint }, new[] { endpoint },
new RouteOptions() { AppendTrailingSlash = false }); new RouteOptions() { AppendTrailingSlash = false });
@ -470,7 +471,7 @@ namespace Microsoft.AspNetCore.Routing
public void RouteGenerationRejectsConstraints() public void RouteGenerationRejectsConstraints()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
"{p1}/{p2}", "{p1}/{p2}",
defaults: new { p2 = "catchall" }, defaults: new { p2 = "catchall" },
constraints: new { p2 = "\\d{4}" }); constraints: new { p2 = "\\d{4}" });
@ -491,7 +492,7 @@ namespace Microsoft.AspNetCore.Routing
public void RouteGenerationAcceptsConstraints() public void RouteGenerationAcceptsConstraints()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
"{p1}/{p2}", "{p1}/{p2}",
defaults: new { p2 = "catchall" }, defaults: new { p2 = "catchall" },
constraints: new { p2 = new RegexRouteConstraint("\\d{4}"), }); constraints: new { p2 = new RegexRouteConstraint("\\d{4}"), });
@ -513,7 +514,7 @@ namespace Microsoft.AspNetCore.Routing
public void RouteWithCatchAllRejectsConstraints() public void RouteWithCatchAllRejectsConstraints()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
"{p1}/{*p2}", "{p1}/{*p2}",
defaults: new { p2 = "catchall" }, defaults: new { p2 = "catchall" },
constraints: new { p2 = new RegexRouteConstraint("\\d{4}") }); constraints: new { p2 = new RegexRouteConstraint("\\d{4}") });
@ -534,7 +535,7 @@ namespace Microsoft.AspNetCore.Routing
public void RouteWithCatchAllAcceptsConstraints() public void RouteWithCatchAllAcceptsConstraints()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
"{p1}/{*p2}", "{p1}/{*p2}",
defaults: new { p2 = "catchall" }, defaults: new { p2 = "catchall" },
constraints: new { p2 = new RegexRouteConstraint("\\d{4}") }); constraints: new { p2 = new RegexRouteConstraint("\\d{4}") });
@ -567,7 +568,7 @@ namespace Microsoft.AspNetCore.Routing
It.IsAny<RouteDirection>())) It.IsAny<RouteDirection>()))
.Returns(true) .Returns(true)
.Verifiable(); .Verifiable();
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
"{p1}/{p2}", "{p1}/{p2}",
defaults: new { p2 = "catchall" }, defaults: new { p2 = "catchall" },
constraints: new { p2 = target.Object }); constraints: new { p2 = target.Object });
@ -593,7 +594,7 @@ namespace Microsoft.AspNetCore.Routing
{ {
// Arrange // Arrange
var constraint = new CapturingConstraint(); var constraint = new CapturingConstraint();
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
template: "slug/Home/Store", template: "slug/Home/Store",
defaults: new { controller = "Home", action = "Store" }, defaults: new { controller = "Home", action = "Store" },
constraints: new { c = constraint }); constraints: new { c = constraint });
@ -622,7 +623,7 @@ namespace Microsoft.AspNetCore.Routing
{ {
// Arrange // Arrange
var constraint = new CapturingConstraint(); var constraint = new CapturingConstraint();
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
template: "slug/Home/Store", template: "slug/Home/Store",
defaults: new { controller = "Home", action = "Store", otherthing = "17" }, defaults: new { controller = "Home", action = "Store", otherthing = "17" },
constraints: new { c = constraint }); constraints: new { c = constraint });
@ -645,7 +646,7 @@ namespace Microsoft.AspNetCore.Routing
{ {
// Arrange // Arrange
var constraint = new CapturingConstraint(); var constraint = new CapturingConstraint();
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
template: "slug/{controller}/{action}", template: "slug/{controller}/{action}",
defaults: new { action = "Index" }, defaults: new { action = "Index" },
constraints: new { c = constraint, }); constraints: new { c = constraint, });
@ -669,7 +670,7 @@ namespace Microsoft.AspNetCore.Routing
{ {
// Arrange // Arrange
var constraint = new CapturingConstraint(); var constraint = new CapturingConstraint();
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
template: "slug/Home/Store", template: "slug/Home/Store",
defaults: new { controller = "Home", action = "Store", otherthing = "17", thirdthing = "13" }, defaults: new { controller = "Home", action = "Store", otherthing = "17", thirdthing = "13" },
constraints: new { c = constraint, }); constraints: new { c = constraint, });
@ -694,7 +695,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_InlineConstraints_Success() public void GetLink_InlineConstraints_Success()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
template: "Home/Index/{id:int}", template: "Home/Index/{id:int}",
defaults: new { controller = "Home", action = "Index" }, defaults: new { controller = "Home", action = "Index" },
constraints: new { }); constraints: new { });
@ -714,7 +715,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_InlineConstraints_NonMatchingvalue() public void GetLink_InlineConstraints_NonMatchingvalue()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
template: "Home/Index/{id}", template: "Home/Index/{id}",
defaults: new { controller = "Home", action = "Index" }, defaults: new { controller = "Home", action = "Index" },
constraints: new { id = "int" }); constraints: new { id = "int" });
@ -735,7 +736,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_InlineConstraints_OptionalParameter_ValuePresent() public void GetLink_InlineConstraints_OptionalParameter_ValuePresent()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
template: "Home/Index/{id:int?}", template: "Home/Index/{id:int?}",
defaults: new { controller = "Home", action = "Index" }, defaults: new { controller = "Home", action = "Index" },
constraints: new { }); constraints: new { });
@ -753,7 +754,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_InlineConstraints_OptionalParameter_ValueNotPresent() public void GetLink_InlineConstraints_OptionalParameter_ValueNotPresent()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
template: "Home/Index/{id?}", template: "Home/Index/{id?}",
defaults: new { controller = "Home", action = "Index" }, defaults: new { controller = "Home", action = "Index" },
constraints: new { id = "int" }); constraints: new { id = "int" });
@ -771,7 +772,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_InlineConstraints_OptionalParameter_ValuePresent_ConstraintFails() public void GetLink_InlineConstraints_OptionalParameter_ValuePresent_ConstraintFails()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
template: "Home/Index/{id?}", template: "Home/Index/{id?}",
defaults: new { controller = "Home", action = "Index" }, defaults: new { controller = "Home", action = "Index" },
constraints: new { id = "int" }); constraints: new { id = "int" });
@ -792,7 +793,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_InlineConstraints_MultipleInlineConstraints() public void GetLink_InlineConstraints_MultipleInlineConstraints()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
template: "Home/Index/{id:int:range(1,20)}", template: "Home/Index/{id:int:range(1,20)}",
defaults: new { controller = "Home", action = "Index" }, defaults: new { controller = "Home", action = "Index" },
constraints: new { }); constraints: new { });
@ -812,7 +813,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_InlineConstraints_CompositeInlineConstraint_Fails() public void GetLink_InlineConstraints_CompositeInlineConstraint_Fails()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
template: "Home/Index/{id:int:range(1,20)}", template: "Home/Index/{id:int:range(1,20)}",
defaults: new { controller = "Home", action = "Index" }, defaults: new { controller = "Home", action = "Index" },
constraints: new { }); constraints: new { });
@ -834,7 +835,7 @@ namespace Microsoft.AspNetCore.Routing
{ {
// Arrange // Arrange
var constraint = new MaxLengthRouteConstraint(20); var constraint = new MaxLengthRouteConstraint(20);
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
template: "Home/Index/{name}", template: "Home/Index/{name}",
defaults: new { controller = "Home", action = "Index" }, defaults: new { controller = "Home", action = "Index" },
constraints: new { name = constraint }); constraints: new { name = constraint });
@ -854,7 +855,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_OptionalParameter_ParameterPresentInValues() public void GetLink_OptionalParameter_ParameterPresentInValues()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}/{name?}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{name?}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
var httpContext = CreateHttpContext(ambientValues: new { }); var httpContext = CreateHttpContext(ambientValues: new { });
@ -871,7 +872,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_OptionalParameter_ParameterNotPresentInValues() public void GetLink_OptionalParameter_ParameterNotPresentInValues()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}/{name?}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{name?}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
var httpContext = CreateHttpContext(ambientValues: new { }); var httpContext = CreateHttpContext(ambientValues: new { });
@ -888,7 +889,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_OptionalParameter_ParameterPresentInValuesAndDefaults() public void GetLink_OptionalParameter_ParameterPresentInValuesAndDefaults()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
template: "{controller}/{action}/{name}", template: "{controller}/{action}/{name}",
defaults: new { name = "default-products" }); defaults: new { name = "default-products" });
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
@ -907,7 +908,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_OptionalParameter_ParameterNotPresentInValues_PresentInDefaults() public void GetLink_OptionalParameter_ParameterNotPresentInValues_PresentInDefaults()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
template: "{controller}/{action}/{name}", template: "{controller}/{action}/{name}",
defaults: new { name = "products" }); defaults: new { name = "products" });
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
@ -926,7 +927,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_ParameterNotPresentInTemplate_PresentInValues() public void GetLink_ParameterNotPresentInTemplate_PresentInValues()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}/{name}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{name}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
var httpContext = CreateHttpContext(ambientValues: new { }); var httpContext = CreateHttpContext(ambientValues: new { });
@ -943,7 +944,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_OptionalParameter_FollowedByDotAfterSlash_ParameterPresent() public void GetLink_OptionalParameter_FollowedByDotAfterSlash_ParameterPresent()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
template: "{controller}/{action}/.{name?}"); template: "{controller}/{action}/.{name?}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
var httpContext = CreateHttpContext(ambientValues: new { }); var httpContext = CreateHttpContext(ambientValues: new { });
@ -961,7 +962,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_OptionalParameter_FollowedByDotAfterSlash_ParameterNotPresent() public void GetLink_OptionalParameter_FollowedByDotAfterSlash_ParameterNotPresent()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}/.{name?}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/.{name?}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
var httpContext = CreateHttpContext(ambientValues: new { }); var httpContext = CreateHttpContext(ambientValues: new { });
@ -976,7 +977,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_OptionalParameter_InSimpleSegment() public void GetLink_OptionalParameter_InSimpleSegment()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}/{name?}"); var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{name?}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
var httpContext = CreateHttpContext(ambientValues: new { }); var httpContext = CreateHttpContext(ambientValues: new { });
@ -991,7 +992,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_TwoOptionalParameters_OneValueFromAmbientValues() public void GetLink_TwoOptionalParameters_OneValueFromAmbientValues()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("a/{b=15}/{c?}/{d?}"); var endpoint = EndpointFactory.CreateRouteEndpoint("a/{b=15}/{c?}/{d?}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
var httpContext = CreateHttpContext(ambientValues: new { c = "17" }); var httpContext = CreateHttpContext(ambientValues: new { c = "17" });
@ -1006,7 +1007,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_OptionalParameterAfterDefault_OneValueFromAmbientValues() public void GetLink_OptionalParameterAfterDefault_OneValueFromAmbientValues()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("a/{b=15}/{c?}"); var endpoint = EndpointFactory.CreateRouteEndpoint("a/{b=15}/{c?}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
var httpContext = CreateHttpContext(ambientValues: new { c = "17" }); var httpContext = CreateHttpContext(ambientValues: new { c = "17" });
@ -1021,7 +1022,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetLink_TwoOptionalParametersAfterDefault_LastValueFromAmbientValues() public void GetLink_TwoOptionalParametersAfterDefault_LastValueFromAmbientValues()
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint("a/{b=15}/{c?}/{d?}"); var endpoint = EndpointFactory.CreateRouteEndpoint("a/{b=15}/{c?}/{d?}");
var linkGenerator = CreateLinkGenerator(endpoint); var linkGenerator = CreateLinkGenerator(endpoint);
var httpContext = CreateHttpContext(ambientValues: new { d = "17" }); var httpContext = CreateHttpContext(ambientValues: new { d = "17" });
@ -1114,7 +1115,7 @@ namespace Microsoft.AspNetCore.Routing
object defaults) object defaults)
{ {
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
"Products/Edit/{id}", "Products/Edit/{id}",
requiredValues: requiredValues, requiredValues: requiredValues,
defaults: defaults); defaults: defaults);
@ -1138,7 +1139,7 @@ namespace Microsoft.AspNetCore.Routing
// Verifying that discarding works in general usage case i.e when keys are not like controller, action etc. // Verifying that discarding works in general usage case i.e when keys are not like controller, action etc.
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
"Products/Edit/{id}", "Products/Edit/{id}",
requiredValues: new { c = "Products", a = "Edit" }, requiredValues: new { c = "Products", a = "Edit" },
defaults: new { c = "Products", a = "Edit" }); defaults: new { c = "Products", a = "Edit" });
@ -1162,7 +1163,7 @@ namespace Microsoft.AspNetCore.Routing
// Verifying that discarding works in general usage case i.e when keys are not like controller, action etc. // Verifying that discarding works in general usage case i.e when keys are not like controller, action etc.
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
"Products/Edit/{id}", "Products/Edit/{id}",
requiredValues: new { c = "Products", a = "Edit" }, requiredValues: new { c = "Products", a = "Edit" },
defaults: new { c = "Products", a = "Edit" }); defaults: new { c = "Products", a = "Edit" });
@ -1268,7 +1269,7 @@ namespace Microsoft.AspNetCore.Routing
// Linking to a different action on the same controller // Linking to a different action on the same controller
// Arrange // Arrange
var endpoint = EndpointFactory.CreateMatcherEndpoint( var endpoint = EndpointFactory.CreateRouteEndpoint(
"Products/Edit/{id}", "Products/Edit/{id}",
requiredValues: requiredValues, requiredValues: requiredValues,
defaults: defaults); defaults: defaults);
@ -1293,11 +1294,11 @@ namespace Microsoft.AspNetCore.Routing
var services = GetBasicServices(); var services = GetBasicServices();
services.TryAddEnumerable( services.TryAddEnumerable(
ServiceDescriptor.Singleton<IEndpointFinder<INameMetadata>, EndpointFinderByName>()); ServiceDescriptor.Singleton<IEndpointFinder<INameMetadata>, EndpointFinderByName>());
var endpoint1 = EndpointFactory.CreateMatcherEndpoint( var endpoint1 = EndpointFactory.CreateRouteEndpoint(
"Products/Details/{id}", "Products/Details/{id}",
requiredValues: new { controller = "Products", action = "Details" }, requiredValues: new { controller = "Products", action = "Details" },
defaults: new { controller = "Products", action = "Details" }); defaults: new { controller = "Products", action = "Details" });
var endpoint2 = EndpointFactory.CreateMatcherEndpoint( var endpoint2 = EndpointFactory.CreateRouteEndpoint(
"Customers/Details/{id}", "Customers/Details/{id}",
requiredValues: new { controller = "Customers", action = "Details" }, requiredValues: new { controller = "Customers", action = "Details" },
defaults: new { controller = "Customers", action = "Details" }, defaults: new { controller = "Customers", action = "Details" },
@ -1324,11 +1325,11 @@ namespace Microsoft.AspNetCore.Routing
var services = GetBasicServices(); var services = GetBasicServices();
services.TryAddEnumerable( services.TryAddEnumerable(
ServiceDescriptor.Singleton<IEndpointFinder<INameMetadata>, EndpointFinderByName>()); ServiceDescriptor.Singleton<IEndpointFinder<INameMetadata>, EndpointFinderByName>());
var endpoint1 = EndpointFactory.CreateMatcherEndpoint( var endpoint1 = EndpointFactory.CreateRouteEndpoint(
"Products/Details/{id}", "Products/Details/{id}",
requiredValues: new { controller = "Products", action = "Details" }, requiredValues: new { controller = "Products", action = "Details" },
defaults: new { controller = "Products", action = "Details" }); defaults: new { controller = "Products", action = "Details" });
var endpoint2 = EndpointFactory.CreateMatcherEndpoint( var endpoint2 = EndpointFactory.CreateRouteEndpoint(
"Customers/Details/{id}", "Customers/Details/{id}",
requiredValues: new { controller = "Customers", action = "Details" }, requiredValues: new { controller = "Customers", action = "Details" },
defaults: new { controller = "Customers", action = "Details" }, defaults: new { controller = "Customers", action = "Details" },
@ -1356,7 +1357,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetTemplate_ByRouteValues_ReturnsTemplate() public void GetTemplate_ByRouteValues_ReturnsTemplate()
{ {
// Arrange // Arrange
var endpoint1 = EndpointFactory.CreateMatcherEndpoint( var endpoint1 = EndpointFactory.CreateRouteEndpoint(
"Product/Edit/{id}", "Product/Edit/{id}",
requiredValues: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null }, requiredValues: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null },
defaults: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null }); defaults: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null });
@ -1379,7 +1380,7 @@ namespace Microsoft.AspNetCore.Routing
public void GetTemplate_ByRouteName_ReturnsTemplate() public void GetTemplate_ByRouteName_ReturnsTemplate()
{ {
// Arrange // Arrange
var endpoint1 = EndpointFactory.CreateMatcherEndpoint( var endpoint1 = EndpointFactory.CreateRouteEndpoint(
"Product/Edit/{id}", "Product/Edit/{id}",
defaults: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null }, defaults: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null },
metadata: new RouteValuesAddressMetadata( metadata: new RouteValuesAddressMetadata(
@ -1403,13 +1404,13 @@ namespace Microsoft.AspNetCore.Routing
public void GetTemplate_ByRouteName_ReturnsTemplate_WithMultipleEndpoints() public void GetTemplate_ByRouteName_ReturnsTemplate_WithMultipleEndpoints()
{ {
// Arrange // Arrange
var endpoint1 = EndpointFactory.CreateMatcherEndpoint( var endpoint1 = EndpointFactory.CreateRouteEndpoint(
"Product/Edit/{id}", "Product/Edit/{id}",
defaults: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null }, defaults: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null },
metadata: new RouteValuesAddressMetadata( metadata: new RouteValuesAddressMetadata(
"default", "default",
new RouteValueDictionary(new { controller = "Product", action = "Edit", area = (string)null, page = (string)null }))); new RouteValueDictionary(new { controller = "Product", action = "Edit", area = (string)null, page = (string)null })));
var endpoint2 = EndpointFactory.CreateMatcherEndpoint( var endpoint2 = EndpointFactory.CreateRouteEndpoint(
"Product/Details/{id}", "Product/Details/{id}",
defaults: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null }, defaults: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null },
metadata: new RouteValuesAddressMetadata( metadata: new RouteValuesAddressMetadata(
@ -1436,11 +1437,11 @@ namespace Microsoft.AspNetCore.Routing
var services = GetBasicServices(); var services = GetBasicServices();
services.TryAddEnumerable( services.TryAddEnumerable(
ServiceDescriptor.Singleton<IEndpointFinder<INameMetadata>, EndpointFinderByName>()); ServiceDescriptor.Singleton<IEndpointFinder<INameMetadata>, EndpointFinderByName>());
var endpoint1 = EndpointFactory.CreateMatcherEndpoint( var endpoint1 = EndpointFactory.CreateRouteEndpoint(
"Product/Edit/{id}", "Product/Edit/{id}",
requiredValues: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null }, requiredValues: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null },
defaults: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null }); defaults: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null });
var endpoint2 = EndpointFactory.CreateMatcherEndpoint( var endpoint2 = EndpointFactory.CreateRouteEndpoint(
"Customers/Details/{id}", "Customers/Details/{id}",
requiredValues: new { controller = "Customers", action = "Details" }, requiredValues: new { controller = "Customers", action = "Details" },
defaults: new { controller = "Customers", action = "Details" }, defaults: new { controller = "Customers", action = "Details" },
@ -1466,11 +1467,11 @@ namespace Microsoft.AspNetCore.Routing
var services = GetBasicServices(); var services = GetBasicServices();
services.TryAddEnumerable( services.TryAddEnumerable(
ServiceDescriptor.Singleton<IEndpointFinder<INameMetadata>, EndpointFinderByName>()); ServiceDescriptor.Singleton<IEndpointFinder<INameMetadata>, EndpointFinderByName>());
var endpoint1 = EndpointFactory.CreateMatcherEndpoint( var endpoint1 = EndpointFactory.CreateRouteEndpoint(
"Product/Edit/{id}", "Product/Edit/{id}",
requiredValues: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null }, requiredValues: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null },
defaults: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null }); defaults: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null });
var endpoint2 = EndpointFactory.CreateMatcherEndpoint( var endpoint2 = EndpointFactory.CreateRouteEndpoint(
"Customers/Details/{id}", "Customers/Details/{id}",
requiredValues: new { controller = "Customers", action = "Details" }, requiredValues: new { controller = "Customers", action = "Details" },
defaults: new { controller = "Customers", action = "Details" }, defaults: new { controller = "Customers", action = "Details" },
@ -1500,7 +1501,7 @@ namespace Microsoft.AspNetCore.Routing
public void MakeUrl_GeneratesLink_WithExtraRouteValues() public void MakeUrl_GeneratesLink_WithExtraRouteValues()
{ {
// Arrange // Arrange
var endpoint1 = EndpointFactory.CreateMatcherEndpoint( var endpoint1 = EndpointFactory.CreateRouteEndpoint(
"Product/Edit/{id}", "Product/Edit/{id}",
requiredValues: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null }, requiredValues: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null },
defaults: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null }); defaults: new { controller = "Product", action = "Edit", area = (string)null, page = (string)null });
@ -1564,10 +1565,13 @@ namespace Microsoft.AspNetCore.Routing
private HttpContext CreateHttpContext(object ambientValues) private HttpContext CreateHttpContext(object ambientValues)
{ {
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
httpContext.Features.Set<IEndpointFeature>(new EndpointFeature
var feature = new EndpointFeature
{ {
Values = new RouteValueDictionary(ambientValues) RouteValues = new RouteValueDictionary(ambientValues)
}); };
httpContext.Features.Set<IEndpointFeature>(feature);
httpContext.Features.Set<IRouteValuesFeature>(feature);
return httpContext; return httpContext;
} }

View File

@ -1,7 +1,7 @@
// 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 Microsoft.AspNetCore.Routing.Matching; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Patterns;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Routing
{ {
internal static class EndpointFactory internal static class EndpointFactory
{ {
public static MatcherEndpoint CreateMatcherEndpoint( public static RouteEndpoint CreateRouteEndpoint(
string template, string template,
object defaults = null, object defaults = null,
object constraints = null, object constraints = null,
@ -25,8 +25,8 @@ namespace Microsoft.AspNetCore.Routing
d.Add(new RouteValuesAddressMetadata(null, new RouteValueDictionary(requiredValues))); d.Add(new RouteValuesAddressMetadata(null, new RouteValueDictionary(requiredValues)));
} }
return new MatcherEndpoint( return new RouteEndpoint(
MatcherEndpoint.EmptyInvoker, TestConstants.EmptyRequestDelegate,
RoutePatternFactory.Parse(template, defaults, constraints), RoutePatternFactory.Parse(template, defaults, constraints),
order, order,
new EndpointMetadataCollection(d), new EndpointMetadataCollection(d),

View File

@ -2,6 +2,7 @@
// 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.Linq; using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Matching; using Microsoft.AspNetCore.Routing.Matching;
using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Patterns;
using Xunit; using Xunit;
@ -18,8 +19,8 @@ namespace Microsoft.AspNetCore.Routing
var feature = new EndpointFeature() var feature = new EndpointFeature()
{ {
Endpoint = new MatcherEndpoint( Endpoint = new RouteEndpoint(
MatcherEndpoint.EmptyInvoker, TestConstants.EmptyRequestDelegate,
RoutePatternFactory.Parse("/"), RoutePatternFactory.Parse("/"),
0, 0,
new EndpointMetadataCollection(new DataTokensMetadata(expected)), new EndpointMetadataCollection(new DataTokensMetadata(expected)),
@ -40,8 +41,8 @@ namespace Microsoft.AspNetCore.Routing
// Arrange // Arrange
var feature = new EndpointFeature() var feature = new EndpointFeature()
{ {
Endpoint = new MatcherEndpoint( Endpoint = new RouteEndpoint(
MatcherEndpoint.EmptyInvoker, TestConstants.EmptyRequestDelegate,
RoutePatternFactory.Parse("/"), RoutePatternFactory.Parse("/"),
0, 0,
new EndpointMetadataCollection(), new EndpointMetadataCollection(),

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing.TestObjects; using Microsoft.AspNetCore.Routing.TestObjects;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
@ -19,8 +20,7 @@ namespace Microsoft.AspNetCore.Routing
public async Task Invoke_OnCall_SetsEndpointFeature() public async Task Invoke_OnCall_SetsEndpointFeature()
{ {
// Arrange // Arrange
var httpContext = new DefaultHttpContext(); var httpContext = CreateHttpContext();
httpContext.RequestServices = new TestServiceProvider();
var middleware = CreateMiddleware(); var middleware = CreateMiddleware();
@ -43,8 +43,7 @@ namespace Microsoft.AspNetCore.Routing
TestSink.EnableWithTypeName<EndpointRoutingMiddleware>); TestSink.EnableWithTypeName<EndpointRoutingMiddleware>);
var loggerFactory = new TestLoggerFactory(sink, enabled: true); var loggerFactory = new TestLoggerFactory(sink, enabled: true);
var httpContext = new DefaultHttpContext(); var httpContext = CreateHttpContext();
httpContext.RequestServices = new TestServiceProvider();
var logger = new Logger<EndpointRoutingMiddleware>(loggerFactory); var logger = new Logger<EndpointRoutingMiddleware>(loggerFactory);
var middleware = CreateMiddleware(logger); var middleware = CreateMiddleware(logger);
@ -62,8 +61,7 @@ namespace Microsoft.AspNetCore.Routing
public async Task Invoke_BackCompatGetRouteValue_ValueUsedFromEndpointFeature() public async Task Invoke_BackCompatGetRouteValue_ValueUsedFromEndpointFeature()
{ {
// Arrange // Arrange
var httpContext = new DefaultHttpContext(); var httpContext = CreateHttpContext();
httpContext.RequestServices = new TestServiceProvider();
var middleware = CreateMiddleware(); var middleware = CreateMiddleware();
@ -71,7 +69,7 @@ namespace Microsoft.AspNetCore.Routing
await middleware.Invoke(httpContext); await middleware.Invoke(httpContext);
var routeData = httpContext.GetRouteData(); var routeData = httpContext.GetRouteData();
var routeValue = httpContext.GetRouteValue("controller"); var routeValue = httpContext.GetRouteValue("controller");
var endpointFeature = httpContext.Features.Get<IEndpointFeature>(); var routeValuesFeature = httpContext.Features.Get<IRouteValuesFeature>();
// Assert // Assert
Assert.NotNull(routeData); Assert.NotNull(routeData);
@ -79,15 +77,14 @@ namespace Microsoft.AspNetCore.Routing
// changing route data value is reflected in endpoint feature values // changing route data value is reflected in endpoint feature values
routeData.Values["testKey"] = "testValue"; routeData.Values["testKey"] = "testValue";
Assert.Equal("testValue", endpointFeature.Values["testKey"]); Assert.Equal("testValue", routeValuesFeature.RouteValues["testKey"]);
} }
[Fact] [Fact]
public async Task Invoke_BackCompatGetDataTokens_ValueUsedFromEndpointMetadata() public async Task Invoke_BackCompatGetDataTokens_ValueUsedFromEndpointMetadata()
{ {
// Arrange // Arrange
var httpContext = new DefaultHttpContext(); var httpContext = CreateHttpContext();
httpContext.RequestServices = new TestServiceProvider();
var middleware = CreateMiddleware(); var middleware = CreateMiddleware();
@ -95,7 +92,7 @@ namespace Microsoft.AspNetCore.Routing
await middleware.Invoke(httpContext); await middleware.Invoke(httpContext);
var routeData = httpContext.GetRouteData(); var routeData = httpContext.GetRouteData();
var routeValue = httpContext.GetRouteValue("controller"); var routeValue = httpContext.GetRouteValue("controller");
var endpointFeature = httpContext.Features.Get<IEndpointFeature>(); var routeValuesFeature = httpContext.Features.Get<IRouteValuesFeature>();
// Assert // Assert
Assert.NotNull(routeData); Assert.NotNull(routeData);
@ -103,7 +100,20 @@ namespace Microsoft.AspNetCore.Routing
// changing route data value is reflected in endpoint feature values // changing route data value is reflected in endpoint feature values
routeData.Values["testKey"] = "testValue"; routeData.Values["testKey"] = "testValue";
Assert.Equal("testValue", endpointFeature.Values["testKey"]); Assert.Equal("testValue", routeValuesFeature.RouteValues["testKey"]);
}
private HttpContext CreateHttpContext()
{
var feature = new EndpointFeature();
var httpContext = new DefaultHttpContext();
httpContext.Features.Set<IEndpointFeature>(feature);
httpContext.Features.Set<IRouteValuesFeature>(feature);
httpContext.RequestServices = new TestServiceProvider();
return httpContext;
} }
private EndpointRoutingMiddleware CreateMiddleware(Logger<EndpointRoutingMiddleware> logger = null) private EndpointRoutingMiddleware CreateMiddleware(Logger<EndpointRoutingMiddleware> logger = null)

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
{ {
@ -19,7 +20,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
Matchers = matchers; Matchers = matchers;
} }
public override Task MatchAsync(HttpContext httpContext, IEndpointFeature feature) public override Task MatchAsync(HttpContext httpContext, EndpointFeature feature)
{ {
if (httpContext == null) if (httpContext == null)
{ {
@ -37,7 +38,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
if (Matchers[i].TryMatch(path)) if (Matchers[i].TryMatch(path))
{ {
feature.Endpoint = Matchers[i].Endpoint; feature.Endpoint = Matchers[i].Endpoint;
feature.Values = new RouteValueDictionary(); feature.RouteValues = new RouteValueDictionary();
} }
} }
@ -46,12 +47,12 @@ namespace Microsoft.AspNetCore.Routing.Matching
public sealed class InnerMatcher : Matcher public sealed class InnerMatcher : Matcher
{ {
public readonly MatcherEndpoint Endpoint; public readonly RouteEndpoint Endpoint;
private readonly string[] _segments; private readonly string[] _segments;
private readonly Candidate[] _candidates; private readonly Candidate[] _candidates;
public InnerMatcher(string[] segments, MatcherEndpoint endpoint) public InnerMatcher(string[] segments, RouteEndpoint endpoint)
{ {
_segments = segments; _segments = segments;
Endpoint = endpoint; Endpoint = endpoint;
@ -120,12 +121,12 @@ namespace Microsoft.AspNetCore.Routing.Matching
return Array.Empty<Candidate>(); return Array.Empty<Candidate>();
} }
public override Task MatchAsync(HttpContext httpContext, IEndpointFeature feature) public override Task MatchAsync(HttpContext httpContext, EndpointFeature feature)
{ {
if (TryMatch(httpContext.Request.Path.Value)) if (TryMatch(httpContext.Request.Path.Value))
{ {
feature.Endpoint = Endpoint; feature.Endpoint = Endpoint;
feature.Values = new RouteValueDictionary(); feature.RouteValues = new RouteValueDictionary();
} }
return Task.CompletedTask; return Task.CompletedTask;

View File

@ -11,9 +11,9 @@ namespace Microsoft.AspNetCore.Routing.Matching
{ {
internal class BarebonesMatcherBuilder : MatcherBuilder internal class BarebonesMatcherBuilder : MatcherBuilder
{ {
private List<MatcherEndpoint> _endpoints = new List<MatcherEndpoint>(); private List<RouteEndpoint> _endpoints = new List<RouteEndpoint>();
public override void AddEndpoint(MatcherEndpoint endpoint) public override void AddEndpoint(RouteEndpoint endpoint)
{ {
_endpoints.Add(endpoint); _endpoints.Add(endpoint);
} }

View File

@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
return Task.CompletedTask; return Task.CompletedTask;
} }
internal override Matcher CreateMatcher(params MatcherEndpoint[] endpoints) internal override Matcher CreateMatcher(params RouteEndpoint[] endpoints)
{ {
var builder = new BarebonesMatcherBuilder(); var builder = new BarebonesMatcherBuilder();
for (int i = 0; i < endpoints.Length; i++) for (int i = 0; i < endpoints.Length; i++)

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Linq; using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Patterns;
using Moq; using Moq;
using Xunit; using Xunit;
@ -24,7 +25,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
public void Create_CreatesCandidateSet(int count) public void Create_CreatesCandidateSet(int count)
{ {
// Arrange // Arrange
var endpoints = new MatcherEndpoint[count]; var endpoints = new RouteEndpoint[count];
for (var i = 0; i < endpoints.Length; i++) for (var i = 0; i < endpoints.Length; i++)
{ {
endpoints[i] = CreateEndpoint($"/{i}"); endpoints[i] = CreateEndpoint($"/{i}");
@ -60,7 +61,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
public void Create_CreatesCandidateSet_TestConstructor(int count) public void Create_CreatesCandidateSet_TestConstructor(int count)
{ {
// Arrange // Arrange
var endpoints = new MatcherEndpoint[count]; var endpoints = new RouteEndpoint[count];
for (var i = 0; i < endpoints.Length; i++) for (var i = 0; i < endpoints.Length; i++)
{ {
endpoints[i] = CreateEndpoint($"/{i}"); endpoints[i] = CreateEndpoint($"/{i}");
@ -80,10 +81,10 @@ namespace Microsoft.AspNetCore.Routing.Matching
} }
} }
private MatcherEndpoint CreateEndpoint(string template) private RouteEndpoint CreateEndpoint(string template)
{ {
return new MatcherEndpoint( return new RouteEndpoint(
MatcherEndpoint.EmptyInvoker, TestConstants.EmptyRequestDelegate,
RoutePatternFactory.Parse(template), RoutePatternFactory.Parse(template),
0, 0,
EndpointMetadataCollection.Empty, EndpointMetadataCollection.Empty,

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.AspNetCore.Routing.TestObjects; using Microsoft.AspNetCore.Routing.TestObjects;
using Xunit; using Xunit;
@ -34,8 +35,8 @@ namespace Microsoft.AspNetCore.Routing.Matching
var dataSource = new DynamicEndpointDataSource(); var dataSource = new DynamicEndpointDataSource();
var matcher = new DataSourceDependentMatcher(dataSource, TestMatcherBuilder.Create); var matcher = new DataSourceDependentMatcher(dataSource, TestMatcherBuilder.Create);
var endpoint = new MatcherEndpoint( var endpoint = new RouteEndpoint(
MatcherEndpoint.EmptyInvoker, TestConstants.EmptyRequestDelegate,
RoutePatternFactory.Parse("a/b/c"), RoutePatternFactory.Parse("a/b/c"),
0, 0,
EndpointMetadataCollection.Empty, EndpointMetadataCollection.Empty,
@ -52,11 +53,11 @@ namespace Microsoft.AspNetCore.Routing.Matching
} }
[Fact] [Fact]
public void Matcher_Ignores_NonMatcherEndpoint() public void Matcher_Ignores_NonRouteEndpoint()
{ {
// Arrange // Arrange
var dataSource = new DynamicEndpointDataSource(); var dataSource = new DynamicEndpointDataSource();
var endpoint = new TestEndpoint(EndpointMetadataCollection.Empty, "test"); var endpoint = new Endpoint(TestConstants.EmptyRequestDelegate, EndpointMetadataCollection.Empty, "test");
dataSource.AddEndpoint(endpoint); dataSource.AddEndpoint(endpoint);
// Act // Act
@ -72,8 +73,8 @@ namespace Microsoft.AspNetCore.Routing.Matching
{ {
// Arrange // Arrange
var dataSource = new DynamicEndpointDataSource(); var dataSource = new DynamicEndpointDataSource();
var endpoint = new MatcherEndpoint( var endpoint = new RouteEndpoint(
MatcherEndpoint.EmptyInvoker, TestConstants.EmptyRequestDelegate,
RoutePatternFactory.Parse("/"), RoutePatternFactory.Parse("/"),
0, 0,
new EndpointMetadataCollection(new SuppressMatchingMetadata()), new EndpointMetadataCollection(new SuppressMatchingMetadata()),
@ -116,9 +117,9 @@ namespace Microsoft.AspNetCore.Routing.Matching
{ {
public static Func<MatcherBuilder> Create = () => new TestMatcherBuilder(); public static Func<MatcherBuilder> Create = () => new TestMatcherBuilder();
private List<MatcherEndpoint> Endpoints { get; } = new List<MatcherEndpoint>(); private List<RouteEndpoint> Endpoints { get; } = new List<RouteEndpoint>();
public override void AddEndpoint(MatcherEndpoint endpoint) public override void AddEndpoint(RouteEndpoint endpoint)
{ {
Endpoints.Add(endpoint); Endpoints.Add(endpoint);
} }
@ -131,9 +132,9 @@ namespace Microsoft.AspNetCore.Routing.Matching
private class TestMatcher : Matcher private class TestMatcher : Matcher
{ {
public IReadOnlyList<MatcherEndpoint> Endpoints { get; set; } public IReadOnlyList<RouteEndpoint> Endpoints { get; set; }
public override Task MatchAsync(HttpContext httpContext, IEndpointFeature feature) public override Task MatchAsync(HttpContext httpContext, EndpointFeature feature)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View File

@ -3,6 +3,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Patterns;
using Moq; using Moq;
using Xunit; using Xunit;
@ -15,7 +16,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
public async Task SelectAsync_NoCandidates_DoesNothing() public async Task SelectAsync_NoCandidates_DoesNothing()
{ {
// Arrange // Arrange
var endpoints = new MatcherEndpoint[] { }; var endpoints = new RouteEndpoint[] { };
var scores = new int[] { }; var scores = new int[] { };
var candidateSet = CreateCandidateSet(endpoints, scores); var candidateSet = CreateCandidateSet(endpoints, scores);
@ -33,7 +34,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
public async Task SelectAsync_NoValidCandidates_DoesNothing() public async Task SelectAsync_NoValidCandidates_DoesNothing()
{ {
// Arrange // Arrange
var endpoints = new MatcherEndpoint[] { CreateEndpoint("/test"), }; var endpoints = new RouteEndpoint[] { CreateEndpoint("/test"), };
var scores = new int[] { 0, }; var scores = new int[] { 0, };
var candidateSet = CreateCandidateSet(endpoints, scores); var candidateSet = CreateCandidateSet(endpoints, scores);
@ -48,14 +49,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
// Assert // Assert
Assert.Null(feature.Endpoint); Assert.Null(feature.Endpoint);
Assert.Null(feature.Values);
} }
[Fact] [Fact]
public async Task SelectAsync_SingleCandidate_ChoosesCandidate() public async Task SelectAsync_SingleCandidate_ChoosesCandidate()
{ {
// Arrange // Arrange
var endpoints = new MatcherEndpoint[] { CreateEndpoint("/test"), }; var endpoints = new RouteEndpoint[] { CreateEndpoint("/test"), };
var scores = new int[] { 0, }; var scores = new int[] { 0, };
var candidateSet = CreateCandidateSet(endpoints, scores); var candidateSet = CreateCandidateSet(endpoints, scores);
@ -70,18 +70,16 @@ namespace Microsoft.AspNetCore.Routing.Matching
// Assert // Assert
Assert.Same(endpoints[0], feature.Endpoint); Assert.Same(endpoints[0], feature.Endpoint);
Assert.Same(endpoints[0].Invoker, feature.Invoker);
Assert.NotNull(feature.Values);
} }
[Fact] [Fact]
public async Task SelectAsync_SingleValidCandidate_ChoosesCandidate() public async Task SelectAsync_SingleValidCandidate_ChoosesCandidate()
{ {
// Arrange // Arrange
var endpoints = new MatcherEndpoint[] { CreateEndpoint("/test1"), CreateEndpoint("/test2"), }; var endpoints = new RouteEndpoint[] { CreateEndpoint("/test1"), CreateEndpoint("/test2"), };
var scores = new int[] { 0, 0 }; var scores = new int[] { 0, 0 };
var candidateSet = CreateCandidateSet(endpoints, scores); var candidateSet = CreateCandidateSet(endpoints, scores);
candidateSet[0].IsValidCandidate = false; candidateSet[0].IsValidCandidate = false;
candidateSet[1].IsValidCandidate = true; candidateSet[1].IsValidCandidate = true;
@ -99,7 +97,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
public async Task SelectAsync_SingleValidCandidateInGroup_ChoosesCandidate() public async Task SelectAsync_SingleValidCandidateInGroup_ChoosesCandidate()
{ {
// Arrange // Arrange
var endpoints = new MatcherEndpoint[] { CreateEndpoint("/test1"), CreateEndpoint("/test2"), CreateEndpoint("/test3"), }; var endpoints = new RouteEndpoint[] { CreateEndpoint("/test1"), CreateEndpoint("/test2"), CreateEndpoint("/test3"), };
var scores = new int[] { 0, 0, 1 }; var scores = new int[] { 0, 0, 1 };
var candidateSet = CreateCandidateSet(endpoints, scores); var candidateSet = CreateCandidateSet(endpoints, scores);
@ -121,7 +119,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
public async Task SelectAsync_ManyGroupsLastCandidate_ChoosesCandidate() public async Task SelectAsync_ManyGroupsLastCandidate_ChoosesCandidate()
{ {
// Arrange // Arrange
var endpoints = new MatcherEndpoint[] var endpoints = new RouteEndpoint[]
{ {
CreateEndpoint("/test1"), CreateEndpoint("/test1"),
CreateEndpoint("/test2"), CreateEndpoint("/test2"),
@ -152,7 +150,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
public async Task SelectAsync_MultipleValidCandidatesInGroup_ReportsAmbiguity() public async Task SelectAsync_MultipleValidCandidatesInGroup_ReportsAmbiguity()
{ {
// Arrange // Arrange
var endpoints = new MatcherEndpoint[] { CreateEndpoint("/test1"), CreateEndpoint("/test2"), CreateEndpoint("/test3"), }; var endpoints = new RouteEndpoint[] { CreateEndpoint("/test1"), CreateEndpoint("/test2"), CreateEndpoint("/test3"), };
var scores = new int[] { 0, 1, 1 }; var scores = new int[] { 0, 1, 1 };
var candidateSet = CreateCandidateSet(endpoints, scores); var candidateSet = CreateCandidateSet(endpoints, scores);
@ -179,7 +177,7 @@ test: /test3", ex.Message);
public async Task SelectAsync_RunsEndpointSelectorPolicies() public async Task SelectAsync_RunsEndpointSelectorPolicies()
{ {
// Arrange // Arrange
var endpoints = new MatcherEndpoint[] { CreateEndpoint("/test1"), CreateEndpoint("/test2"), CreateEndpoint("/test3"), }; var endpoints = new RouteEndpoint[] { CreateEndpoint("/test1"), CreateEndpoint("/test2"), CreateEndpoint("/test3"), };
var scores = new int[] { 0, 0, 1 }; var scores = new int[] { 0, 0, 1 };
var candidateSet = CreateCandidateSet(endpoints, scores); var candidateSet = CreateCandidateSet(endpoints, scores);
@ -206,22 +204,27 @@ test: /test3", ex.Message);
Assert.Same(endpoints[2], feature.Endpoint); Assert.Same(endpoints[2], feature.Endpoint);
} }
private static (HttpContext httpContext, IEndpointFeature feature) CreateContext() private static (HttpContext httpContext, EndpointFeature feature) CreateContext()
{ {
return (new DefaultHttpContext(), new EndpointFeature()); var feature = new EndpointFeature();
var httpContext = new DefaultHttpContext();
httpContext.Features.Set<IEndpointFeature>(feature);
httpContext.Features.Set<IRouteValuesFeature>(feature);
return (httpContext, feature);
} }
private static MatcherEndpoint CreateEndpoint(string template) private static RouteEndpoint CreateEndpoint(string template)
{ {
return new MatcherEndpoint( return new RouteEndpoint(
MatcherEndpoint.EmptyInvoker, TestConstants.EmptyRequestDelegate,
RoutePatternFactory.Parse(template), RoutePatternFactory.Parse(template),
0, 0,
EndpointMetadataCollection.Empty, EndpointMetadataCollection.Empty,
$"test: {template}"); $"test: {template}");
} }
private static CandidateSet CreateCandidateSet(MatcherEndpoint[] endpoints, int[] scores) private static CandidateSet CreateCandidateSet(RouteEndpoint[] endpoints, int[] scores)
{ {
return new CandidateSet(endpoints, scores); return new CandidateSet(endpoints, scores);
} }
@ -231,4 +234,4 @@ test: /test3", ex.Message);
return new DefaultEndpointSelector(policies); return new DefaultEndpointSelector(policies);
} }
} }
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Constraints; using Microsoft.AspNetCore.Routing.Constraints;
using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -876,14 +877,14 @@ namespace Microsoft.AspNetCore.Routing.Matching
policies); policies);
} }
private MatcherEndpoint CreateEndpoint( private RouteEndpoint CreateEndpoint(
string template, string template,
object defaults = null, object defaults = null,
object constraints = null, object constraints = null,
params object[] metadata) params object[] metadata)
{ {
return new MatcherEndpoint( return new RouteEndpoint(
MatcherEndpoint.EmptyInvoker, TestConstants.EmptyRequestDelegate,
RoutePatternFactory.Parse(template, new RouteValueDictionary(defaults), new RouteValueDictionary(constraints)), RoutePatternFactory.Parse(template, new RouteValueDictionary(defaults), new RouteValueDictionary(constraints)),
0, 0,
new EndpointMetadataCollection(metadata), new EndpointMetadataCollection(metadata),

View File

@ -20,10 +20,10 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint, keys, values); MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values);
} }
internal override Matcher CreateMatcher(params MatcherEndpoint[] endpoints) internal override Matcher CreateMatcher(params RouteEndpoint[] endpoints)
{ {
var services = new ServiceCollection() var services = new ServiceCollection()
.AddLogging() .AddLogging()

View File

@ -4,6 +4,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Moq; using Moq;
@ -15,10 +16,10 @@ namespace Microsoft.AspNetCore.Routing.Matching
// so we're reusing the services here. // so we're reusing the services here.
public class DfaMatcherTest public class DfaMatcherTest
{ {
private MatcherEndpoint CreateEndpoint(string template, int order, object defaults = null, EndpointMetadataCollection metadata = null) private RouteEndpoint CreateEndpoint(string template, int order, object defaults = null, EndpointMetadataCollection metadata = null)
{ {
return new MatcherEndpoint( return new RouteEndpoint(
MatcherEndpoint.EmptyInvoker, TestConstants.EmptyRequestDelegate,
RoutePatternFactory.Parse(template, defaults, parameterPolicies: null), RoutePatternFactory.Parse(template, defaults, parameterPolicies: null),
order, order,
metadata ?? EndpointMetadataCollection.Empty, metadata ?? EndpointMetadataCollection.Empty,
@ -54,11 +55,9 @@ namespace Microsoft.AspNetCore.Routing.Matching
var matcher = CreateDfaMatcher(endpointDataSource); var matcher = CreateDfaMatcher(endpointDataSource);
var httpContext = new DefaultHttpContext(); var (httpContext, endpointFeature) = CreateHttpContext();
httpContext.Request.Path = "/1"; httpContext.Request.Path = "/1";
var endpointFeature = new EndpointFeature();
// Act // Act
await matcher.MatchAsync(httpContext, endpointFeature); await matcher.MatchAsync(httpContext, endpointFeature);
@ -77,11 +76,9 @@ namespace Microsoft.AspNetCore.Routing.Matching
var matcher = CreateDfaMatcher(endpointDataSource); var matcher = CreateDfaMatcher(endpointDataSource);
var httpContext = new DefaultHttpContext(); var (httpContext, endpointFeature) = CreateHttpContext();
httpContext.Request.Path = "/One"; httpContext.Request.Path = "/One";
var endpointFeature = new EndpointFeature();
// Act // Act
await matcher.MatchAsync(httpContext, endpointFeature); await matcher.MatchAsync(httpContext, endpointFeature);
@ -104,11 +101,9 @@ namespace Microsoft.AspNetCore.Routing.Matching
var matcher = CreateDfaMatcher(endpointDataSource); var matcher = CreateDfaMatcher(endpointDataSource);
var httpContext = new DefaultHttpContext(); var (httpContext, endpointFeature) = CreateHttpContext();
httpContext.Request.Path = "/Teams"; httpContext.Request.Path = "/Teams";
var endpointFeature = new EndpointFeature();
// Act // Act
await matcher.MatchAsync(httpContext, endpointFeature); await matcher.MatchAsync(httpContext, endpointFeature);
@ -125,7 +120,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
var endpointSelector = new Mock<EndpointSelector>(); var endpointSelector = new Mock<EndpointSelector>();
endpointSelector endpointSelector
.Setup(s => s.SelectAsync(It.IsAny<HttpContext>(), It.IsAny<IEndpointFeature>(), It.IsAny<CandidateSet>())) .Setup(s => s.SelectAsync(It.IsAny<HttpContext>(), It.IsAny<EndpointFeature>(), It.IsAny<CandidateSet>()))
.Callback<HttpContext, IEndpointFeature, CandidateSet>((c, f, cs) => .Callback<HttpContext, IEndpointFeature, CandidateSet>((c, f, cs) =>
{ {
Assert.Equal(2, cs.Count); Assert.Equal(2, cs.Count);
@ -152,16 +147,25 @@ namespace Microsoft.AspNetCore.Routing.Matching
var matcher = CreateDfaMatcher(endpointDataSource, endpointSelector.Object); var matcher = CreateDfaMatcher(endpointDataSource, endpointSelector.Object);
var httpContext = new DefaultHttpContext(); var (httpContext, endpointFeature) = CreateHttpContext();
httpContext.Request.Path = "/Teams"; httpContext.Request.Path = "/Teams";
var endpointFeature = new EndpointFeature();
// Act // Act
await matcher.MatchAsync(httpContext, endpointFeature); await matcher.MatchAsync(httpContext, endpointFeature);
// Assert // Assert
Assert.Equal(endpoint2, endpointFeature.Endpoint); Assert.Equal(endpoint2, endpointFeature.Endpoint);
} }
private (HttpContext httpContext, EndpointFeature feature) CreateHttpContext()
{
var feature = new EndpointFeature();
var httpContext = new DefaultHttpContext();
httpContext.Features.Set<IEndpointFeature>(feature);
httpContext.Features.Set<IRouteValuesFeature>(feature);
return (httpContext, feature);
}
} }
} }

View File

@ -2,7 +2,7 @@
// 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.Collections.Generic;
using Microsoft.AspNetCore.Routing.TestObjects; using Microsoft.AspNetCore.Http;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
@ -13,8 +13,8 @@ namespace Microsoft.AspNetCore.Routing.Matching
public void Compare_EndpointWithMetadata_MoreSpecific() public void Compare_EndpointWithMetadata_MoreSpecific()
{ {
// Arrange // Arrange
var endpoint1 = new TestEndpoint(new EndpointMetadataCollection(new object[] { new TestMetadata(), }), "test1"); var endpoint1 = new Endpoint(TestConstants.EmptyRequestDelegate, new EndpointMetadataCollection(new object[] { new TestMetadata(), }), "test1");
var endpoint2 = new TestEndpoint(new EndpointMetadataCollection(new object[] { }), "test2"); var endpoint2 = new Endpoint(TestConstants.EmptyRequestDelegate, new EndpointMetadataCollection(new object[] { }), "test2");
// Act // Act
var result = EndpointMetadataComparer<TestMetadata>.Default.Compare(endpoint1, endpoint2); var result = EndpointMetadataComparer<TestMetadata>.Default.Compare(endpoint1, endpoint2);
@ -27,8 +27,8 @@ namespace Microsoft.AspNetCore.Routing.Matching
public void Compare_EndpointWithMetadata_ReverseOrder_MoreSpecific() public void Compare_EndpointWithMetadata_ReverseOrder_MoreSpecific()
{ {
// Arrange // Arrange
var endpoint1 = new TestEndpoint(new EndpointMetadataCollection(new object[] { }), "test1"); var endpoint1 = new Endpoint(TestConstants.EmptyRequestDelegate, new EndpointMetadataCollection(new object[] { }), "test1");
var endpoint2 = new TestEndpoint(new EndpointMetadataCollection(new object[] { new TestMetadata(), }), "test2"); var endpoint2 = new Endpoint(TestConstants.EmptyRequestDelegate, new EndpointMetadataCollection(new object[] { new TestMetadata(), }), "test2");
// Act // Act
var result = EndpointMetadataComparer<TestMetadata>.Default.Compare(endpoint1, endpoint2); var result = EndpointMetadataComparer<TestMetadata>.Default.Compare(endpoint1, endpoint2);
@ -41,8 +41,8 @@ namespace Microsoft.AspNetCore.Routing.Matching
public void Compare_BothEndpointsWithMetadata_Equal() public void Compare_BothEndpointsWithMetadata_Equal()
{ {
// Arrange // Arrange
var endpoint1 = new TestEndpoint(new EndpointMetadataCollection(new object[] { new TestMetadata(), }), "test1"); var endpoint1 = new Endpoint(TestConstants.EmptyRequestDelegate, new EndpointMetadataCollection(new object[] { new TestMetadata(), }), "test1");
var endpoint2 = new TestEndpoint(new EndpointMetadataCollection(new object[] { new TestMetadata(), }), "test2"); var endpoint2 = new Endpoint(TestConstants.EmptyRequestDelegate, new EndpointMetadataCollection(new object[] { new TestMetadata(), }), "test2");
// Act // Act
var result = EndpointMetadataComparer<TestMetadata>.Default.Compare(endpoint1, endpoint2); var result = EndpointMetadataComparer<TestMetadata>.Default.Compare(endpoint1, endpoint2);
@ -55,8 +55,8 @@ namespace Microsoft.AspNetCore.Routing.Matching
public void Compare_BothEndpointsWithoutMetadata_Equal() public void Compare_BothEndpointsWithoutMetadata_Equal()
{ {
// Arrange // Arrange
var endpoint1 = new TestEndpoint(new EndpointMetadataCollection(new object[] { }), "test1"); var endpoint1 = new Endpoint(TestConstants.EmptyRequestDelegate, new EndpointMetadataCollection(new object[] { }), "test1");
var endpoint2 = new TestEndpoint(new EndpointMetadataCollection(new object[] { }), "test2"); var endpoint2 = new Endpoint(TestConstants.EmptyRequestDelegate, new EndpointMetadataCollection(new object[] { }), "test2");
// Act // Act
var result = EndpointMetadataComparer<TestMetadata>.Default.Compare(endpoint1, endpoint2); var result = EndpointMetadataComparer<TestMetadata>.Default.Compare(endpoint1, endpoint2);
@ -69,8 +69,8 @@ namespace Microsoft.AspNetCore.Routing.Matching
public void Sort_EndpointWithMetadata_FirstInList() public void Sort_EndpointWithMetadata_FirstInList()
{ {
// Arrange // Arrange
var endpoint1 = new TestEndpoint(new EndpointMetadataCollection(new object[] { new TestMetadata(), }), "test1"); var endpoint1 = new Endpoint(TestConstants.EmptyRequestDelegate, new EndpointMetadataCollection(new object[] { new TestMetadata(), }), "test1");
var endpoint2 = new TestEndpoint(new EndpointMetadataCollection(new object[] { }), "test2"); var endpoint2 = new Endpoint(TestConstants.EmptyRequestDelegate, new EndpointMetadataCollection(new object[] { }), "test2");
var list = new List<Endpoint>() { endpoint2, endpoint1, }; var list = new List<Endpoint>() { endpoint2, endpoint1, };

View File

@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint, keys, values); MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values);
} }
[Fact] [Fact]
@ -49,7 +49,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint, new { b = "17", c = "18", }); MatcherAssert.AssertMatch(feature, httpContext, endpoint, new { b = "17", c = "18", });
} }
[Fact] [Fact]
@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint, new { b = "17", c = "18", d = "19" }); MatcherAssert.AssertMatch(feature, httpContext, endpoint, new { b = "17", c = "18", d = "19" });
} }
[Theory] [Theory]
@ -89,7 +89,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertNotMatch(feature); MatcherAssert.AssertNotMatch(feature, httpContext);
} }
[Theory] [Theory]
@ -121,7 +121,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint, keys, values); MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values);
} }
[Theory] [Theory]
@ -144,7 +144,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertNotMatch(feature); MatcherAssert.AssertNotMatch(feature, httpContext);
} }
[Theory] [Theory]
@ -168,7 +168,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint, keys, values); MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values);
} }
// Historically catchall segments don't match an empty segment, but only if it's // Historically catchall segments don't match an empty segment, but only if it's
@ -188,7 +188,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertNotMatch(feature); MatcherAssert.AssertNotMatch(feature, httpContext);
// Need to access these to prevent a warning from the xUnit analyzer. // Need to access these to prevent a warning from the xUnit analyzer.
// Some of these tests will match (and process the values) and some will not. // Some of these tests will match (and process the values) and some will not.
@ -223,7 +223,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint, keys, values); MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values);
} }
[Theory] [Theory]
@ -243,7 +243,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertNotMatch(feature); MatcherAssert.AssertNotMatch(feature, httpContext);
} }
[Theory] [Theory]
@ -272,7 +272,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint, keys, values); MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values);
} }
[Theory] [Theory]
@ -298,7 +298,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertNotMatch(feature); MatcherAssert.AssertNotMatch(feature, httpContext);
} }
// Most of are copied from old routing tests that date back to the VS 2010 era. Enjoy! // Most of are copied from old routing tests that date back to the VS 2010 era. Enjoy!
@ -323,7 +323,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint, keys, values); MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values);
} }
[Theory] [Theory]
@ -353,7 +353,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, expected, ignoreValues: true); MatcherAssert.AssertMatch(feature, httpContext, expected, ignoreValues: true);
} }
[Theory] [Theory]
@ -387,7 +387,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, expected, ignoreValues: true); MatcherAssert.AssertMatch(feature, httpContext, expected, ignoreValues: true);
} }
[Theory] [Theory]
@ -440,7 +440,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, expected, ignoreValues: true); MatcherAssert.AssertMatch(feature, httpContext, expected, ignoreValues: true);
} }
} }
} }

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Xunit; using Xunit;
@ -28,7 +29,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint); MatcherAssert.AssertMatch(feature, httpContext, endpoint);
} }
[Fact] [Fact]
@ -44,7 +45,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint); MatcherAssert.AssertMatch(feature, httpContext, endpoint);
} }
[Fact] [Fact]
@ -60,7 +61,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint); MatcherAssert.AssertMatch(feature, httpContext, endpoint);
} }
@ -94,7 +95,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint); MatcherAssert.AssertMatch(feature, httpContext, endpoint);
} }
[Fact] [Fact]
@ -110,7 +111,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint); MatcherAssert.AssertMatch(feature, httpContext, endpoint);
} }
[Fact] [Fact]
@ -126,7 +127,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint); MatcherAssert.AssertMatch(feature, httpContext, endpoint);
} }
[Fact] [Fact]
@ -142,7 +143,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint); MatcherAssert.AssertMatch(feature, httpContext, endpoint);
} }
[Fact] // This matches because the endpoint accepts OPTIONS [Fact] // This matches because the endpoint accepts OPTIONS
@ -158,7 +159,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint); MatcherAssert.AssertMatch(feature, httpContext, endpoint);
} }
[Fact] [Fact]
@ -174,7 +175,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint); MatcherAssert.AssertMatch(feature, httpContext, endpoint);
} }
[Fact] // When all of the candidates handles specific verbs, use a 405 endpoint [Fact] // When all of the candidates handles specific verbs, use a 405 endpoint
@ -197,7 +198,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
Assert.Same(HttpMethodMatcherPolicy.Http405EndpointDisplayName, feature.Endpoint.DisplayName); Assert.Same(HttpMethodMatcherPolicy.Http405EndpointDisplayName, feature.Endpoint.DisplayName);
// Invoke the endpoint // Invoke the endpoint
await feature.Invoker((c) => Task.CompletedTask)(httpContext); await feature.Endpoint.RequestDelegate(httpContext);
Assert.Equal(405, httpContext.Response.StatusCode); Assert.Equal(405, httpContext.Response.StatusCode);
Assert.Equal("DELETE, GET, PUT", httpContext.Response.Headers["Allow"]); Assert.Equal("DELETE, GET, PUT", httpContext.Response.Headers["Allow"]);
} }
@ -216,7 +217,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertNotMatch(feature); MatcherAssert.AssertNotMatch(feature, httpContext);
} }
[Fact] // When one of the candidates handles all verbs, dont use a 405 endpoint [Fact] // When one of the candidates handles all verbs, dont use a 405 endpoint
@ -233,7 +234,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertNotMatch(feature); MatcherAssert.AssertNotMatch(feature, httpContext);
} }
[Fact] [Fact]
@ -250,7 +251,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint1); MatcherAssert.AssertMatch(feature, httpContext, endpoint1);
} }
[Fact] [Fact]
@ -267,7 +268,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint1); MatcherAssert.AssertMatch(feature, httpContext, endpoint1);
} }
[Fact] // The non-http-method-specific endpoint is part of the same candidate set [Fact] // The non-http-method-specific endpoint is part of the same candidate set
@ -284,10 +285,10 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint2, ignoreValues: true); MatcherAssert.AssertMatch(feature, httpContext, endpoint2, ignoreValues: true);
} }
private static Matcher CreateMatcher(params MatcherEndpoint[] endpoints) private static Matcher CreateMatcher(params RouteEndpoint[] endpoints)
{ {
var services = new ServiceCollection() var services = new ServiceCollection()
.AddOptions() .AddOptions()
@ -304,7 +305,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
return builder.Build(); return builder.Build();
} }
internal static (HttpContext httpContext, IEndpointFeature feature) CreateContext( internal static (HttpContext httpContext, EndpointFeature feature) CreateContext(
string path, string path,
string httpMethod, string httpMethod,
bool corsPreflight = false) bool corsPreflight = false)
@ -321,10 +322,11 @@ namespace Microsoft.AspNetCore.Routing.Matching
var feature = new EndpointFeature(); var feature = new EndpointFeature();
httpContext.Features.Set<IEndpointFeature>(feature); httpContext.Features.Set<IEndpointFeature>(feature);
httpContext.Features.Set<IRouteValuesFeature>(feature);
return (httpContext, feature); return (httpContext, feature);
} }
internal static MatcherEndpoint CreateEndpoint( internal static RouteEndpoint CreateEndpoint(
string template, string template,
object defaults = null, object defaults = null,
object constraints = null, object constraints = null,
@ -339,15 +341,15 @@ namespace Microsoft.AspNetCore.Routing.Matching
} }
var displayName = "endpoint: " + template + " " + string.Join(", ", httpMethods ?? new[] { "(any)" }); var displayName = "endpoint: " + template + " " + string.Join(", ", httpMethods ?? new[] { "(any)" });
return new MatcherEndpoint( return new RouteEndpoint(
MatcherEndpoint.EmptyInvoker, TestConstants.EmptyRequestDelegate,
RoutePatternFactory.Parse(template, defaults, constraints), RoutePatternFactory.Parse(template, defaults, constraints),
order, order,
new EndpointMetadataCollection(metadata), new EndpointMetadataCollection(metadata),
displayName); displayName);
} }
internal (Matcher matcher, MatcherEndpoint endpoint) CreateMatcher(string template) internal (Matcher matcher, RouteEndpoint endpoint) CreateMatcher(string template)
{ {
var endpoint = CreateEndpoint(template); var endpoint = CreateEndpoint(template);
return (CreateMatcher(endpoint), endpoint); return (CreateMatcher(endpoint), endpoint);

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Patterns;
using Xunit; using Xunit;
using static Microsoft.AspNetCore.Routing.Matching.HttpMethodMatcherPolicy; using static Microsoft.AspNetCore.Routing.Matching.HttpMethodMatcherPolicy;
@ -276,7 +277,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
}); });
} }
private static MatcherEndpoint CreateEndpoint(string template, HttpMethodMetadata httpMethodMetadata) private static RouteEndpoint CreateEndpoint(string template, HttpMethodMetadata httpMethodMetadata)
{ {
var metadata = new List<object>(); var metadata = new List<object>();
if (httpMethodMetadata != null) if (httpMethodMetadata != null)
@ -284,8 +285,8 @@ namespace Microsoft.AspNetCore.Routing.Matching
metadata.Add(httpMethodMetadata); metadata.Add(httpMethodMetadata);
} }
return new MatcherEndpoint( return new RouteEndpoint(
MatcherEndpoint.EmptyInvoker, TestConstants.EmptyRequestDelegate,
RoutePatternFactory.Parse(template), RoutePatternFactory.Parse(template),
0, 0,
new EndpointMetadataCollection(metadata), new EndpointMetadataCollection(metadata),

View File

@ -4,28 +4,30 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Xunit.Sdk; using Xunit.Sdk;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
{ {
internal static class MatcherAssert internal static class MatcherAssert
{ {
public static void AssertMatch(IEndpointFeature feature, Endpoint expected) public static void AssertMatch(EndpointFeature feature, HttpContext context, Endpoint expected)
{ {
AssertMatch(feature, expected, new RouteValueDictionary()); AssertMatch(feature, context, expected, new RouteValueDictionary());
} }
public static void AssertMatch(IEndpointFeature feature, Endpoint expected, bool ignoreValues) public static void AssertMatch(EndpointFeature feature, HttpContext context, Endpoint expected, bool ignoreValues)
{ {
AssertMatch(feature, expected, new RouteValueDictionary(), ignoreValues); AssertMatch(feature, context, expected, new RouteValueDictionary(), ignoreValues);
} }
public static void AssertMatch(IEndpointFeature feature, Endpoint expected, object values) public static void AssertMatch(EndpointFeature feature, HttpContext context, Endpoint expected, object values)
{ {
AssertMatch(feature, expected, new RouteValueDictionary(values)); AssertMatch(feature, context, expected, new RouteValueDictionary(values));
} }
public static void AssertMatch(IEndpointFeature feature, Endpoint expected, string[] keys, string[] values) public static void AssertMatch(EndpointFeature feature, HttpContext context, Endpoint expected, string[] keys, string[] values)
{ {
keys = keys ?? Array.Empty<string>(); keys = keys ?? Array.Empty<string>();
values = values ?? Array.Empty<string>(); values = values ?? Array.Empty<string>();
@ -36,11 +38,12 @@ namespace Microsoft.AspNetCore.Routing.Matching
} }
var zipped = keys.Zip(values, (k, v) => new KeyValuePair<string, object>(k, v)); var zipped = keys.Zip(values, (k, v) => new KeyValuePair<string, object>(k, v));
AssertMatch(feature, expected, new RouteValueDictionary(zipped)); AssertMatch(feature, context, expected, new RouteValueDictionary(zipped));
} }
public static void AssertMatch( public static void AssertMatch(
IEndpointFeature feature, EndpointFeature feature,
HttpContext context,
Endpoint expected, Endpoint expected,
RouteValueDictionary values, RouteValueDictionary values,
bool ignoreValues = false) bool ignoreValues = false)
@ -50,39 +53,41 @@ namespace Microsoft.AspNetCore.Routing.Matching
throw new XunitException($"Was expected to match '{expected.DisplayName}' but did not match."); throw new XunitException($"Was expected to match '{expected.DisplayName}' but did not match.");
} }
if (feature.Values == null) var actualValues = context.Features.Get<IRouteValuesFeature>().RouteValues;
if (actualValues == null)
{ {
throw new XunitException("Values is null."); throw new XunitException("RouteValues is null.");
} }
if (!object.ReferenceEquals(expected, feature.Endpoint)) if (!object.ReferenceEquals(expected, feature.Endpoint))
{ {
throw new XunitException( throw new XunitException(
$"Was expected to match '{expected.DisplayName}' but matched " + $"Was expected to match '{expected.DisplayName}' but matched " +
$"'{feature.Endpoint.DisplayName}' with values: {FormatRouteValues(feature.Values)}."); $"'{feature.Endpoint.DisplayName}' with values: {FormatRouteValues(actualValues)}.");
} }
if (!ignoreValues) if (!ignoreValues)
{ {
// Note: this comparison is intended for unit testing, and is stricter than necessary to make tests // Note: this comparison is intended for unit testing, and is stricter than necessary to make tests
// more precise. Route value comparisons in product code are more flexible than a simple .Equals. // more precise. Route value comparisons in product code are more flexible than a simple .Equals.
if (values.Count != feature.Values.Count || if (values.Count != actualValues.Count ||
!values.OrderBy(kvp => kvp.Key).SequenceEqual(feature.Values.OrderBy(kvp => kvp.Key))) !values.OrderBy(kvp => kvp.Key).SequenceEqual(actualValues.OrderBy(kvp => kvp.Key)))
{ {
throw new XunitException( throw new XunitException(
$"Was expected to match '{expected.DisplayName}' with values {FormatRouteValues(values)} but matched " + $"Was expected to match '{expected.DisplayName}' with values {FormatRouteValues(values)} but matched " +
$"values: {FormatRouteValues(feature.Values)}."); $"values: {FormatRouteValues(actualValues)}.");
} }
} }
} }
public static void AssertNotMatch(IEndpointFeature feature) public static void AssertNotMatch(EndpointFeature feature, HttpContext context)
{ {
if (feature.Endpoint != null) if (feature.Endpoint != null)
{ {
throw new XunitException( throw new XunitException(
$"Was expected not to match '{feature.Endpoint.DisplayName}' " + $"Was expected not to match '{feature.Endpoint.DisplayName}' " +
$"but matched with values: {FormatRouteValues(feature.Values)}."); $"but matched with values: {FormatRouteValues(context.Features.Get<IRouteValuesFeature>().RouteValues)}.");
} }
} }
@ -91,4 +96,4 @@ namespace Microsoft.AspNetCore.Routing.Matching
return "{" + string.Join(", ", values.Select(kvp => $"{kvp.Key} = '{kvp.Value}'")) + "}"; return "{" + string.Join(", ", values.Select(kvp => $"{kvp.Key} = '{kvp.Value}'")) + "}";
} }
} }
} }

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint); MatcherAssert.AssertMatch(feature, httpContext, endpoint);
} }
[Fact] [Fact]
@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint); MatcherAssert.AssertMatch(feature, httpContext, endpoint);
} }
[Fact] [Fact]
@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint); MatcherAssert.AssertMatch(feature, httpContext, endpoint);
} }
[Theory] [Theory]
@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint); MatcherAssert.AssertMatch(feature, httpContext, endpoint);
} }
// Some matchers will optimize for the ASCII case // Some matchers will optimize for the ASCII case
@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint); MatcherAssert.AssertMatch(feature, httpContext, endpoint);
} }
// Matchers should operate on the decoded representation - a matcher that calls // Matchers should operate on the decoded representation - a matcher that calls
@ -99,7 +99,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint); MatcherAssert.AssertMatch(feature, httpContext, endpoint);
} }
[Theory] [Theory]
@ -119,7 +119,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertNotMatch(feature); MatcherAssert.AssertNotMatch(feature, httpContext);
} }
[Theory] [Theory]
@ -136,7 +136,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint); MatcherAssert.AssertMatch(feature, httpContext, endpoint);
} }
// Matchers do their own 'splitting' of the path into segments, so including // Matchers do their own 'splitting' of the path into segments, so including
@ -159,7 +159,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint); MatcherAssert.AssertMatch(feature, httpContext, endpoint);
} }
// Matchers do their own 'splitting' of the path into segments, so including // Matchers do their own 'splitting' of the path into segments, so including
@ -190,7 +190,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertNotMatch(feature); MatcherAssert.AssertNotMatch(feature, httpContext);
} }
[Fact] [Fact]
@ -205,7 +205,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint, values); MatcherAssert.AssertMatch(feature, httpContext, endpoint, values);
} }
[Fact] [Fact]
@ -220,7 +220,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint, values); MatcherAssert.AssertMatch(feature, httpContext, endpoint, values);
} }
[Fact] [Fact]
@ -235,7 +235,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint, values); MatcherAssert.AssertMatch(feature, httpContext, endpoint, values);
} }
[Fact] [Fact]
@ -255,7 +255,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint, values); MatcherAssert.AssertMatch(feature, httpContext, endpoint, values);
} }
[Theory] [Theory]
@ -273,7 +273,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertNotMatch(feature); MatcherAssert.AssertNotMatch(feature, httpContext);
} }
[Theory] [Theory]
@ -294,7 +294,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertMatch(feature, endpoint, keys, values); MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values);
} }
[Theory] [Theory]
@ -323,7 +323,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
await matcher.MatchAsync(httpContext, feature); await matcher.MatchAsync(httpContext, feature);
// Assert // Assert
MatcherAssert.AssertNotMatch(feature); MatcherAssert.AssertNotMatch(feature, httpContext);
} }
} }
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -11,9 +12,9 @@ namespace Microsoft.AspNetCore.Routing.Matching
{ {
public abstract partial class MatcherConformanceTest public abstract partial class MatcherConformanceTest
{ {
internal abstract Matcher CreateMatcher(params MatcherEndpoint[] endpoints); internal abstract Matcher CreateMatcher(params RouteEndpoint[] endpoints);
internal static (HttpContext httpContext, IEndpointFeature feature) CreateContext(string path) internal static (HttpContext httpContext, EndpointFeature feature) CreateContext(string path)
{ {
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
httpContext.Request.Method = "TEST"; httpContext.Request.Method = "TEST";
@ -22,6 +23,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
var feature = new EndpointFeature(); var feature = new EndpointFeature();
httpContext.Features.Set<IEndpointFeature>(feature); httpContext.Features.Set<IEndpointFeature>(feature);
httpContext.Features.Set<IRouteValuesFeature>(feature);
return (httpContext, feature); return (httpContext, feature);
} }
@ -34,21 +36,21 @@ namespace Microsoft.AspNetCore.Routing.Matching
return services.BuildServiceProvider(); return services.BuildServiceProvider();
} }
internal static MatcherEndpoint CreateEndpoint( internal static RouteEndpoint CreateEndpoint(
string template, string template,
object defaults = null, object defaults = null,
object constraints = null, object constraints = null,
int? order = null) int? order = null)
{ {
return new MatcherEndpoint( return new RouteEndpoint(
MatcherEndpoint.EmptyInvoker, TestConstants.EmptyRequestDelegate,
RoutePatternFactory.Parse(template, defaults, constraints), RoutePatternFactory.Parse(template, defaults, constraints),
order ?? 0, order ?? 0,
EndpointMetadataCollection.Empty, EndpointMetadataCollection.Empty,
"endpoint: " + template); "endpoint: " + template);
} }
internal (Matcher matcher, MatcherEndpoint endpoint) CreateMatcher(string template) internal (Matcher matcher, RouteEndpoint endpoint) CreateMatcher(string template)
{ {
var endpoint = CreateEndpoint(template); var endpoint = CreateEndpoint(template);
return (CreateMatcher(endpoint), endpoint); return (CreateMatcher(endpoint), endpoint);

View File

@ -2,12 +2,13 @@
// 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.Collections.Generic;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Patterns;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
{ {
public class MatcherEndpointComparerTest public class RouteEndpointComparerTest
{ {
[Fact] [Fact]
public void Compare_PrefersOrder_IfDifferent() public void Compare_PrefersOrder_IfDifferent()
@ -198,7 +199,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
var endpoint7 = CreateEndpoint("/bar{baz}", order: 0, new TestMetadata1(), new TestMetadata2()); var endpoint7 = CreateEndpoint("/bar{baz}", order: 0, new TestMetadata1(), new TestMetadata2());
// Endpoints listed in reverse of the desired order. // Endpoints listed in reverse of the desired order.
var list = new List<MatcherEndpoint>() { endpoint7, endpoint6, endpoint5, endpoint4, endpoint3, endpoint2, endpoint1, }; var list = new List<RouteEndpoint>() { endpoint7, endpoint6, endpoint5, endpoint4, endpoint3, endpoint2, endpoint1, };
var comparer = CreateComparer(new TestMetadata1Policy(), new TestMetadata2Policy()); var comparer = CreateComparer(new TestMetadata1Policy(), new TestMetadata2Policy());
@ -217,19 +218,19 @@ namespace Microsoft.AspNetCore.Routing.Matching
e => Assert.Same(endpoint7, e)); e => Assert.Same(endpoint7, e));
} }
private static MatcherEndpoint CreateEndpoint(string template, int order, params object[] metadata) private static RouteEndpoint CreateEndpoint(string template, int order, params object[] metadata)
{ {
return new MatcherEndpoint( return new RouteEndpoint(
MatcherEndpoint.EmptyInvoker, TestConstants.EmptyRequestDelegate,
RoutePatternFactory.Parse(template), RoutePatternFactory.Parse(template),
order, order,
new EndpointMetadataCollection(metadata), new EndpointMetadataCollection(metadata),
"test: " + template); "test: " + template);
} }
private static MatcherEndpointComparer CreateComparer(params IEndpointComparerPolicy[] policies) private static RouteEndpointComparer CreateComparer(params IEndpointComparerPolicy[] policies)
{ {
return new MatcherEndpointComparer(policies); return new RouteEndpointComparer(policies);
} }
private class TestMetadata1 private class TestMetadata1

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
{ {
@ -17,7 +18,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
_inner = inner; _inner = inner;
} }
public async override Task MatchAsync(HttpContext httpContext, IEndpointFeature feature) public async override Task MatchAsync(HttpContext httpContext, EndpointFeature feature)
{ {
if (httpContext == null) if (httpContext == null)
{ {
@ -34,7 +35,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
if (context.Handler != null) if (context.Handler != null)
{ {
feature.Values = context.RouteData.Values; feature.RouteValues = context.RouteData.Values;
await context.Handler(httpContext); await context.Handler(httpContext);
} }
} }

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -13,15 +14,15 @@ namespace Microsoft.AspNetCore.Routing.Matching
internal class RouteMatcherBuilder : MatcherBuilder internal class RouteMatcherBuilder : MatcherBuilder
{ {
private readonly IInlineConstraintResolver _constraintResolver; private readonly IInlineConstraintResolver _constraintResolver;
private readonly List<MatcherEndpoint> _endpoints; private readonly List<RouteEndpoint> _endpoints;
public RouteMatcherBuilder() public RouteMatcherBuilder()
{ {
_constraintResolver = new DefaultInlineConstraintResolver(Options.Create(new RouteOptions())); _constraintResolver = new DefaultInlineConstraintResolver(Options.Create(new RouteOptions()));
_endpoints = new List<MatcherEndpoint>(); _endpoints = new List<RouteEndpoint>();
} }
public override void AddEndpoint(MatcherEndpoint endpoint) public override void AddEndpoint(RouteEndpoint endpoint)
{ {
_endpoints.Add(endpoint); _endpoints.Add(endpoint);
} }
@ -73,10 +74,10 @@ namespace Microsoft.AspNetCore.Routing.Matching
private class SelectorRouter : IRouter private class SelectorRouter : IRouter
{ {
private readonly EndpointSelector _selector; private readonly EndpointSelector _selector;
private readonly MatcherEndpoint[] _candidates; private readonly RouteEndpoint[] _candidates;
private readonly int[] _scores; private readonly int[] _scores;
public SelectorRouter(EndpointSelector selector, MatcherEndpoint[] candidates) public SelectorRouter(EndpointSelector selector, RouteEndpoint[] candidates)
{ {
_selector = selector; _selector = selector;
_candidates = candidates; _candidates = candidates;
@ -91,7 +92,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
public async Task RouteAsync(RouteContext context) public async Task RouteAsync(RouteContext context)
{ {
var feature = context.HttpContext.Features.Get<IEndpointFeature>(); var feature = (EndpointFeature)context.HttpContext.Features.Get<IEndpointFeature>();
// This is needed due to a quirk of our tests - they reuse the endpoint feature // This is needed due to a quirk of our tests - they reuse the endpoint feature
// across requests. // across requests.

View File

@ -5,7 +5,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
{ {
public class RouteMatcherConformanceTest : FullFeaturedMatcherConformanceTest public class RouteMatcherConformanceTest : FullFeaturedMatcherConformanceTest
{ {
internal override Matcher CreateMatcher(params MatcherEndpoint[] endpoints) internal override Matcher CreateMatcher(params RouteEndpoint[] endpoints)
{ {
var builder = new RouteMatcherBuilder(); var builder = new RouteMatcherBuilder();
for (int i = 0; i < endpoints.Length; i++) for (int i = 0; i < endpoints.Length; i++)

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing.Tree; using Microsoft.AspNetCore.Routing.Tree;
namespace Microsoft.AspNetCore.Routing.Matching namespace Microsoft.AspNetCore.Routing.Matching
@ -18,7 +19,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
_inner = inner; _inner = inner;
} }
public async override Task MatchAsync(HttpContext httpContext, IEndpointFeature feature) public async override Task MatchAsync(HttpContext httpContext, EndpointFeature feature)
{ {
if (httpContext == null) if (httpContext == null)
{ {
@ -35,7 +36,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
if (context.Handler != null) if (context.Handler != null)
{ {
feature.Values = context.RouteData.Values; feature.RouteValues = context.RouteData.Values;
await context.Handler(httpContext); await context.Handler(httpContext);
} }
} }

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing.Internal; using Microsoft.AspNetCore.Routing.Internal;
using Microsoft.AspNetCore.Routing.Template; using Microsoft.AspNetCore.Routing.Template;
using Microsoft.AspNetCore.Routing.Tree; using Microsoft.AspNetCore.Routing.Tree;
@ -16,14 +17,14 @@ namespace Microsoft.AspNetCore.Routing.Matching
{ {
internal class TreeRouterMatcherBuilder : MatcherBuilder internal class TreeRouterMatcherBuilder : MatcherBuilder
{ {
private readonly List<MatcherEndpoint> _endpoints; private readonly List<RouteEndpoint> _endpoints;
public TreeRouterMatcherBuilder() public TreeRouterMatcherBuilder()
{ {
_endpoints = new List<MatcherEndpoint>(); _endpoints = new List<RouteEndpoint>();
} }
public override void AddEndpoint(MatcherEndpoint endpoint) public override void AddEndpoint(RouteEndpoint endpoint)
{ {
_endpoints.Add(endpoint); _endpoints.Add(endpoint);
} }
@ -48,7 +49,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
{ {
var candidates = group.ToArray(); var candidates = group.ToArray();
// MatcherEndpoint.Values contains the default values parsed from the template // RouteEndpoint.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 endpoint = group.First(); var endpoint = group.First();
@ -75,10 +76,10 @@ namespace Microsoft.AspNetCore.Routing.Matching
private class SelectorRouter : IRouter private class SelectorRouter : IRouter
{ {
private readonly EndpointSelector _selector; private readonly EndpointSelector _selector;
private readonly MatcherEndpoint[] _candidates; private readonly RouteEndpoint[] _candidates;
private readonly int[] _scores; private readonly int[] _scores;
public SelectorRouter(EndpointSelector selector, MatcherEndpoint[] candidates) public SelectorRouter(EndpointSelector selector, RouteEndpoint[] candidates)
{ {
_selector = selector; _selector = selector;
_candidates = candidates; _candidates = candidates;
@ -93,7 +94,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
public async Task RouteAsync(RouteContext context) public async Task RouteAsync(RouteContext context)
{ {
var feature = context.HttpContext.Features.Get<IEndpointFeature>(); var feature = (EndpointFeature)context.HttpContext.Features.Get<IEndpointFeature>();
// This is needed due to a quirk of our tests - they reuse the endpoint feature. // This is needed due to a quirk of our tests - they reuse the endpoint feature.
feature.Endpoint = null; feature.Endpoint = null;

View File

@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
return Task.CompletedTask; return Task.CompletedTask;
} }
internal override Matcher CreateMatcher(params MatcherEndpoint[] endpoints) internal override Matcher CreateMatcher(params RouteEndpoint[] endpoints)
{ {
var builder = new TreeRouterMatcherBuilder(); var builder = new TreeRouterMatcherBuilder();
for (var i = 0; i < endpoints.Length; i++) for (var i = 0; i < endpoints.Length; i++)

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Internal; using Microsoft.AspNetCore.Routing.Internal;
using Microsoft.AspNetCore.Routing.Matching; using Microsoft.AspNetCore.Routing.Matching;
using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Patterns;
@ -34,7 +35,7 @@ namespace Microsoft.AspNetCore.Routing
Assert.NotNull(finder.NamedMatches); Assert.NotNull(finder.NamedMatches);
Assert.True(finder.NamedMatches.TryGetValue("named", out var namedMatches)); Assert.True(finder.NamedMatches.TryGetValue("named", out var namedMatches));
var namedMatch = Assert.Single(namedMatches); var namedMatch = Assert.Single(namedMatches);
var actual = Assert.IsType<MatcherEndpoint>(namedMatch.Match.Entry.Data); var actual = Assert.IsType<RouteEndpoint>(namedMatch.Match.Entry.Data);
Assert.Same(endpoint2, actual); Assert.Same(endpoint2, actual);
} }
@ -55,8 +56,8 @@ namespace Microsoft.AspNetCore.Routing
Assert.NotNull(finder.NamedMatches); Assert.NotNull(finder.NamedMatches);
Assert.True(finder.NamedMatches.TryGetValue("named", out var namedMatches)); Assert.True(finder.NamedMatches.TryGetValue("named", out var namedMatches));
Assert.Equal(2, namedMatches.Count); Assert.Equal(2, namedMatches.Count);
Assert.Same(endpoint2, Assert.IsType<MatcherEndpoint>(namedMatches[0].Match.Entry.Data)); Assert.Same(endpoint2, Assert.IsType<RouteEndpoint>(namedMatches[0].Match.Entry.Data));
Assert.Same(endpoint3, Assert.IsType<MatcherEndpoint>(namedMatches[1].Match.Entry.Data)); Assert.Same(endpoint3, Assert.IsType<RouteEndpoint>(namedMatches[1].Match.Entry.Data));
} }
[Fact] [Fact]
@ -76,8 +77,8 @@ namespace Microsoft.AspNetCore.Routing
Assert.NotNull(finder.NamedMatches); Assert.NotNull(finder.NamedMatches);
Assert.True(finder.NamedMatches.TryGetValue("named", out var namedMatches)); Assert.True(finder.NamedMatches.TryGetValue("named", out var namedMatches));
Assert.Equal(2, namedMatches.Count); Assert.Equal(2, namedMatches.Count);
Assert.Same(endpoint2, Assert.IsType<MatcherEndpoint>(namedMatches[0].Match.Entry.Data)); Assert.Same(endpoint2, Assert.IsType<RouteEndpoint>(namedMatches[0].Match.Entry.Data));
Assert.Same(endpoint3, Assert.IsType<MatcherEndpoint>(namedMatches[1].Match.Entry.Data)); Assert.Same(endpoint3, Assert.IsType<RouteEndpoint>(namedMatches[1].Match.Entry.Data));
} }
[Fact] [Fact]
@ -97,7 +98,7 @@ namespace Microsoft.AspNetCore.Routing
// Assert 1 // Assert 1
Assert.NotNull(finder.AllMatches); Assert.NotNull(finder.AllMatches);
var match = Assert.Single(finder.AllMatches); var match = Assert.Single(finder.AllMatches);
var actual = Assert.IsType<MatcherEndpoint>(match.Entry.Data); var actual = Assert.IsType<RouteEndpoint>(match.Entry.Data);
Assert.Same(endpoint1, actual); Assert.Same(endpoint1, actual);
// Arrange 2 // Arrange 2
@ -127,22 +128,22 @@ namespace Microsoft.AspNetCore.Routing
finder.AllMatches, finder.AllMatches,
(m) => (m) =>
{ {
actual = Assert.IsType<MatcherEndpoint>(m.Entry.Data); actual = Assert.IsType<RouteEndpoint>(m.Entry.Data);
Assert.Same(endpoint1, actual); Assert.Same(endpoint1, actual);
}, },
(m) => (m) =>
{ {
actual = Assert.IsType<MatcherEndpoint>(m.Entry.Data); actual = Assert.IsType<RouteEndpoint>(m.Entry.Data);
Assert.Same(endpoint2, actual); Assert.Same(endpoint2, actual);
}, },
(m) => (m) =>
{ {
actual = Assert.IsType<MatcherEndpoint>(m.Entry.Data); actual = Assert.IsType<RouteEndpoint>(m.Entry.Data);
Assert.Same(endpoint3, actual); Assert.Same(endpoint3, actual);
}, },
(m) => (m) =>
{ {
actual = Assert.IsType<MatcherEndpoint>(m.Entry.Data); actual = Assert.IsType<RouteEndpoint>(m.Entry.Data);
Assert.Same(endpoint4, actual); Assert.Same(endpoint4, actual);
}); });
} }
@ -231,7 +232,7 @@ namespace Microsoft.AspNetCore.Routing
objectPool); objectPool);
} }
private MatcherEndpoint CreateEndpoint( private RouteEndpoint CreateEndpoint(
string template, string template,
object defaults = null, object defaults = null,
object requiredValues = null, object requiredValues = null,
@ -249,8 +250,8 @@ namespace Microsoft.AspNetCore.Routing
metadataCollection = new EndpointMetadataCollection(metadata); metadataCollection = new EndpointMetadataCollection(metadata);
} }
return new MatcherEndpoint( return new RouteEndpoint(
MatcherEndpoint.EmptyInvoker, TestConstants.EmptyRequestDelegate,
RoutePatternFactory.Parse(template, defaults, parameterPolicies: null), RoutePatternFactory.Parse(template, defaults, parameterPolicies: null),
order, order,
metadataCollection, metadataCollection,

View File

@ -0,0 +1,13 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing
{
public static class TestConstants
{
internal static readonly RequestDelegate EmptyRequestDelegate = (context) => Task.CompletedTask;
}
}

View File

@ -3,6 +3,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Routing.TestObjects namespace Microsoft.AspNetCore.Routing.TestObjects

View File

@ -1,13 +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.
namespace Microsoft.AspNetCore.Routing.TestObjects
{
internal class TestEndpoint : Endpoint
{
public TestEndpoint(EndpointMetadataCollection metadata, string displayName)
: base(metadata, displayName)
{
}
}
}

View File

@ -3,6 +3,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing.Matching; using Microsoft.AspNetCore.Routing.Matching;
namespace Microsoft.AspNetCore.Routing.TestObjects namespace Microsoft.AspNetCore.Routing.TestObjects
@ -16,12 +17,12 @@ namespace Microsoft.AspNetCore.Routing.TestObjects
_isHandled = isHandled; _isHandled = isHandled;
} }
public override Task MatchAsync(HttpContext httpContext, IEndpointFeature feature) public override Task MatchAsync(HttpContext httpContext, EndpointFeature feature)
{ {
if (_isHandled) if (_isHandled)
{ {
feature.Values = new RouteValueDictionary(new { controller = "Home", action = "Index" }); feature.RouteValues = new RouteValueDictionary(new { controller = "Home", action = "Index" });
feature.Endpoint = new TestEndpoint(EndpointMetadataCollection.Empty, "Test endpoint"); feature.Endpoint = new Endpoint(TestConstants.EmptyRequestDelegate, EndpointMetadataCollection.Empty, "Test endpoint");
} }
return Task.CompletedTask; return Task.CompletedTask;