Merge pull request #830 from aspnet/feature/endpoint-selector-master
Feature/endpoint selector master
This commit is contained in:
commit
e833352572
|
|
@ -129,7 +129,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
protected (HttpContext httpContext, RouteValueDictionary ambientValues) CreateCurrentRequestContext(
|
||||
object ambientValues = null)
|
||||
{
|
||||
var feature = new EndpointFeature { RouteValues = new RouteValueDictionary(ambientValues) };
|
||||
var feature = new EndpointSelectorContext { RouteValues = new RouteValueDictionary(ambientValues) };
|
||||
var context = new DefaultHttpContext();
|
||||
context.Features.Set<IEndpointFeature>(feature);
|
||||
context.Features.Set<IRouteValuesFeature>(feature);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
private Matcher _dfa;
|
||||
|
||||
private int[] _samples;
|
||||
private EndpointFeature _feature;
|
||||
private EndpointSelectorContext _feature;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
|
|
@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
_baseline = (BarebonesMatcher)SetupMatcher(new BarebonesMatcherBuilder());
|
||||
_dfa = SetupMatcher(CreateDfaMatcherBuilder());
|
||||
|
||||
_feature = new EndpointFeature();
|
||||
_feature = new EndpointSelectorContext();
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true, OperationsPerInvoke = SampleCount)]
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
_baseline = (TrivialMatcher)SetupMatcher(new TrivialMatcherBuilder());
|
||||
_dfa = (DfaMatcher)SetupMatcher(CreateDfaMatcherBuilder());
|
||||
|
||||
_feature = new EndpointFeature();
|
||||
_feature = new EndpointSelectorContext();
|
||||
}
|
||||
|
||||
private void SetupEndpoints()
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
private BarebonesMatcher _baseline;
|
||||
private Matcher _dfa;
|
||||
|
||||
private EndpointFeature _feature;
|
||||
private EndpointSelectorContext _feature;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
|
|
@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
_baseline = (BarebonesMatcher)SetupMatcher(new BarebonesMatcherBuilder());
|
||||
_dfa = SetupMatcher(CreateDfaMatcherBuilder());
|
||||
|
||||
_feature = new EndpointFeature();
|
||||
_feature = new EndpointSelectorContext();
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true, OperationsPerInvoke = EndpointCount)]
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
private Matcher _route;
|
||||
private Matcher _tree;
|
||||
|
||||
private EndpointFeature _feature;
|
||||
private EndpointSelectorContext _feature;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
|
|
@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
_route = SetupMatcher(new RouteMatcherBuilder());
|
||||
_tree = SetupMatcher(new TreeRouterMatcherBuilder());
|
||||
|
||||
_feature = new EndpointFeature();
|
||||
_feature = new EndpointSelectorContext();
|
||||
}
|
||||
|
||||
private Matcher SetupMatcher(MatcherBuilder builder)
|
||||
|
|
|
|||
|
|
@ -23,23 +23,23 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
_candidates = new Candidate[] { new Candidate(endpoint), };
|
||||
}
|
||||
|
||||
public sealed override Task MatchAsync(HttpContext httpContext, EndpointFeature feature)
|
||||
public sealed override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context)
|
||||
{
|
||||
if (httpContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(httpContext));
|
||||
}
|
||||
|
||||
if (feature == null)
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(feature));
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var path = httpContext.Request.Path.Value;
|
||||
if (string.Equals(_endpoint.RoutePattern.RawText, path, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
feature.Endpoint = _endpoint;
|
||||
feature.RouteValues = new RouteValueDictionary();
|
||||
context.Endpoint = _endpoint;
|
||||
context.RouteValues = new RouteValueDictionary();
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
<MicrosoftExtensionsWebEncodersPackageVersion>3.0.0-alpha1-10549</MicrosoftExtensionsWebEncodersPackageVersion>
|
||||
<MicrosoftNETCoreApp20PackageVersion>2.0.9</MicrosoftNETCoreApp20PackageVersion>
|
||||
<MicrosoftNETCoreApp21PackageVersion>2.1.3</MicrosoftNETCoreApp21PackageVersion>
|
||||
<MicrosoftNETCoreApp22PackageVersion>2.2.0-preview2-26905-02</MicrosoftNETCoreApp22PackageVersion>
|
||||
<MicrosoftNETCoreApp22PackageVersion>2.2.0-preview3-26927-02</MicrosoftNETCoreApp22PackageVersion>
|
||||
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
|
||||
<MoqPackageVersion>4.9.0</MoqPackageVersion>
|
||||
<NETStandardLibrary20PackageVersion>2.0.3</NETStandardLibrary20PackageVersion>
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
var feature = new EndpointFeature();
|
||||
var feature = new EndpointSelectorContext();
|
||||
|
||||
// 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.
|
||||
|
|
@ -66,7 +66,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
{
|
||||
// Set the endpoint feature only on success. This means we won't overwrite any
|
||||
// existing state for related features unless we did something.
|
||||
SetEndpointFeature(httpContext, feature);
|
||||
SetFeatures(httpContext, feature);
|
||||
|
||||
Log.MatchSuccess(_logger, feature);
|
||||
}
|
||||
|
|
@ -78,13 +78,13 @@ namespace Microsoft.AspNetCore.Routing
|
|||
await _next(httpContext);
|
||||
}
|
||||
|
||||
private static void SetEndpointFeature(HttpContext httpContext, EndpointFeature feature)
|
||||
private static void SetFeatures(HttpContext httpContext, EndpointSelectorContext context)
|
||||
{
|
||||
// For back-compat EndpointRouteValuesFeature implements IEndpointFeature,
|
||||
// For back-compat EndpointSelectorContext implements IEndpointFeature,
|
||||
// IRouteValuesFeature and IRoutingFeature
|
||||
httpContext.Features.Set<IRoutingFeature>(feature);
|
||||
httpContext.Features.Set<IRouteValuesFeature>(feature);
|
||||
httpContext.Features.Set<IEndpointFeature>(feature);
|
||||
httpContext.Features.Set<IRoutingFeature>(context);
|
||||
httpContext.Features.Set<IRouteValuesFeature>(context);
|
||||
httpContext.Features.Set<IEndpointFeature>(context);
|
||||
}
|
||||
|
||||
// Initialization is async to avoid blocking threads while reflection and things
|
||||
|
|
@ -126,9 +126,9 @@ namespace Microsoft.AspNetCore.Routing
|
|||
new EventId(2, "MatchFailure"),
|
||||
"Request did not match any endpoints.");
|
||||
|
||||
public static void MatchSuccess(ILogger logger, EndpointFeature feature)
|
||||
public static void MatchSuccess(ILogger logger, EndpointSelectorContext context)
|
||||
{
|
||||
_matchSuccess(logger, feature.Endpoint.DisplayName, null);
|
||||
_matchSuccess(logger, context.Endpoint.DisplayName, null);
|
||||
}
|
||||
|
||||
public static void MatchFailure(ILogger logger)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Http.Features;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
public sealed class EndpointFeature : IEndpointFeature, IRouteValuesFeature, IRoutingFeature
|
||||
public sealed class EndpointSelectorContext : IEndpointFeature, IRouteValuesFeature, IRoutingFeature
|
||||
{
|
||||
private RouteData _routeData;
|
||||
private RouteValueDictionary _routeValues;
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
|
|
@ -14,6 +16,12 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
/// </summary>
|
||||
public sealed class CandidateSet
|
||||
{
|
||||
private const int BitVectorSize = 32;
|
||||
|
||||
// Cannot be readonly because we need to modify it in place.
|
||||
private BitVector32 _validity;
|
||||
private readonly BitArray _largeCapactityValidity;
|
||||
|
||||
// We inline storage for 4 candidates here to avoid allocations in common
|
||||
// cases. There's no real reason why 4 is important, it just seemed like
|
||||
// a plausible number.
|
||||
|
|
@ -26,8 +34,8 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Initializes a new instances of the candidate set structure with the provided list of endpoints
|
||||
/// and associated scores.
|
||||
/// Initializes a new instances of the <see cref="CandidateSet"/> class with the provided <paramref name="endpoints"/>,
|
||||
/// <paramref name="values"/>, and <paramref name="scores"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The constructor is provided to enable unit tests of implementations of <see cref="EndpointSelector"/>
|
||||
|
|
@ -35,9 +43,30 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="endpoints">The list of endpoints, sorted in descending priority order.</param>
|
||||
/// <param name="values">The list of <see cref="RouteValueDictionary"/> instances.</param>
|
||||
/// <param name="scores">The list of endpoint scores. <see cref="CandidateState.Score"/>.</param>
|
||||
public CandidateSet(Endpoint[] endpoints, int[] scores)
|
||||
public CandidateSet(Endpoint[] endpoints, RouteValueDictionary[] values, int[] scores)
|
||||
{
|
||||
if (endpoints == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(endpoints));
|
||||
}
|
||||
|
||||
if (values == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(values));
|
||||
}
|
||||
|
||||
if (scores == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(scores));
|
||||
}
|
||||
|
||||
if (endpoints.Length != values.Length || endpoints.Length != scores.Length)
|
||||
{
|
||||
throw new ArgumentException($"The provided {nameof(endpoints)}, {nameof(values)}, and {nameof(scores)} must have the same length.");
|
||||
}
|
||||
|
||||
Count = endpoints.Length;
|
||||
|
||||
switch (endpoints.Length)
|
||||
|
|
@ -46,40 +75,51 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
return;
|
||||
|
||||
case 1:
|
||||
_state0 = new CandidateState(endpoints[0], score: scores[0]);
|
||||
_state0 = new CandidateState(endpoints[0], values[0], scores[0]);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
_state0 = new CandidateState(endpoints[0], score: scores[0]);
|
||||
_state1 = new CandidateState(endpoints[1], score: scores[1]);
|
||||
_state0 = new CandidateState(endpoints[0], values[0], scores[0]);
|
||||
_state1 = new CandidateState(endpoints[1], values[1], scores[1]);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
_state0 = new CandidateState(endpoints[0], score: scores[0]);
|
||||
_state1 = new CandidateState(endpoints[1], score: scores[1]);
|
||||
_state2 = new CandidateState(endpoints[2], score: scores[2]);
|
||||
_state0 = new CandidateState(endpoints[0], values[0], scores[0]);
|
||||
_state1 = new CandidateState(endpoints[1], values[1], scores[1]);
|
||||
_state2 = new CandidateState(endpoints[2], values[2], scores[2]);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
_state0 = new CandidateState(endpoints[0], score: scores[0]);
|
||||
_state1 = new CandidateState(endpoints[1], score: scores[1]);
|
||||
_state2 = new CandidateState(endpoints[2], score: scores[2]);
|
||||
_state3 = new CandidateState(endpoints[3], score: scores[3]);
|
||||
_state0 = new CandidateState(endpoints[0], values[0], scores[0]);
|
||||
_state1 = new CandidateState(endpoints[1], values[1], scores[1]);
|
||||
_state2 = new CandidateState(endpoints[2], values[2], scores[2]);
|
||||
_state3 = new CandidateState(endpoints[3], values[3], scores[3]);
|
||||
break;
|
||||
|
||||
default:
|
||||
_state0 = new CandidateState(endpoints[0], score: scores[0]);
|
||||
_state1 = new CandidateState(endpoints[1], score: scores[1]);
|
||||
_state2 = new CandidateState(endpoints[2], score: scores[2]);
|
||||
_state3 = new CandidateState(endpoints[3], score: scores[3]);
|
||||
_state0 = new CandidateState(endpoints[0], values[0], scores[0]);
|
||||
_state1 = new CandidateState(endpoints[1], values[1], scores[1]);
|
||||
_state2 = new CandidateState(endpoints[2], values[2], scores[2]);
|
||||
_state3 = new CandidateState(endpoints[3], values[3], scores[3]);
|
||||
|
||||
_additionalCandidates = new CandidateState[endpoints.Length - 4];
|
||||
for (var i = 4; i < endpoints.Length; i++)
|
||||
{
|
||||
_additionalCandidates[i - 4] = new CandidateState(endpoints[i], score: scores[i]);
|
||||
_additionalCandidates[i - 4] = new CandidateState(endpoints[i], values[i], scores[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Initialize validity to valid by default.
|
||||
if (Count < BitVectorSize)
|
||||
{
|
||||
// Sets the bit for each candidate that exists (bits > Count will be 0).
|
||||
_validity = new BitVector32(unchecked((int)~(0xFFFFFFFFu << Count)));
|
||||
}
|
||||
else
|
||||
{
|
||||
_largeCapactityValidity = new BitArray(Count, defaultValue: true);
|
||||
}
|
||||
}
|
||||
|
||||
internal CandidateSet(Candidate[] candidates)
|
||||
|
|
@ -126,6 +166,17 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Initialize validity to valid by default.
|
||||
if (Count < BitVectorSize)
|
||||
{
|
||||
// Sets the bit for each candidate that exists (bits > Count will be 0).
|
||||
_validity = new BitVector32(unchecked((int)~(0xFFFFFFFFu << Count)));
|
||||
}
|
||||
else
|
||||
{
|
||||
_largeCapactityValidity = new BitArray(Count, defaultValue: true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -139,12 +190,11 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
/// </summary>
|
||||
/// <param name="index">The candidate index.</param>
|
||||
/// <returns>
|
||||
/// A reference to the <see cref="CandidateState"/>. The result is returned by reference
|
||||
/// and intended to be mutated.
|
||||
/// A reference to the <see cref="CandidateState"/>. The result is returned by reference.
|
||||
/// </returns>
|
||||
public ref CandidateState this[int index]
|
||||
{
|
||||
// Note that this is a ref-return because of both mutability and performance.
|
||||
// Note that this is a ref-return because of performance.
|
||||
// We don't want to copy these fat structs if it can be avoided.
|
||||
|
||||
// PERF: Force inlining
|
||||
|
|
@ -177,6 +227,60 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value which indicates where the <see cref="Http.Endpoint"/> is considered
|
||||
/// a valid candiate for the current request.
|
||||
/// </summary>
|
||||
/// <param name="index">The candidate index.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the candidate at position <paramref name="index"/> is considered value
|
||||
/// for the current request, otherwise <c>false</c>.
|
||||
/// </returns>
|
||||
public bool IsValidCandidate(int index)
|
||||
{
|
||||
// Friendliness for inlining
|
||||
if ((uint)index >= Count)
|
||||
{
|
||||
ThrowIndexArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
if (Count < BitVectorSize)
|
||||
{
|
||||
// Get the n-th bit
|
||||
return _validity[0x00000001 << index];
|
||||
}
|
||||
else
|
||||
{
|
||||
return _largeCapactityValidity[index];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the validitity of the candidate at the provided index.
|
||||
/// </summary>
|
||||
/// <param name="index">The candidate index.</param>
|
||||
/// <param name="value">
|
||||
/// The value to set. If <c>true</c> the candidate is considered valid for the current request.
|
||||
/// </param>
|
||||
public void SetValidity(int index, bool value)
|
||||
{
|
||||
// Friendliness for inlining
|
||||
if ((uint)index >= Count)
|
||||
{
|
||||
ThrowIndexArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
if (Count < BitVectorSize)
|
||||
{
|
||||
// Set the n-th bit
|
||||
_validity[0x00000001 << index] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_largeCapactityValidity[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ThrowIndexArgumentOutOfRangeException()
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Http;
|
|||
namespace Microsoft.AspNetCore.Routing.Matching
|
||||
{
|
||||
/// <summary>
|
||||
/// The mutable state associated with a candidate in a <see cref="CandidateSet"/>.
|
||||
/// The state associated with a candidate in a <see cref="CandidateSet"/>.
|
||||
/// </summary>
|
||||
public struct CandidateState
|
||||
{
|
||||
|
|
@ -14,11 +14,16 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
Endpoint = endpoint;
|
||||
Score = score;
|
||||
|
||||
IsValidCandidate = true;
|
||||
Values = null;
|
||||
}
|
||||
|
||||
internal CandidateState(Endpoint endpoint, RouteValueDictionary values, int score)
|
||||
{
|
||||
Endpoint = endpoint;
|
||||
Values = values;
|
||||
Score = score;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Http.Endpoint"/>.
|
||||
/// </summary>
|
||||
|
|
@ -42,16 +47,9 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
public int Score { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// <see cref="Http.Endpoint"/> from consideration.
|
||||
/// </summary>
|
||||
public bool IsValidCandidate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="RouteValueDictionary"/> associated with the
|
||||
/// Gets <see cref="RouteValueDictionary"/> associated with the
|
||||
/// <see cref="Http.Endpoint"/> and the current request.
|
||||
/// </summary>
|
||||
public RouteValueDictionary Values { get; set; }
|
||||
public RouteValueDictionary Values { get; internal set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
// Used in tests
|
||||
internal Matcher CurrentMatcher => _cache.Value;
|
||||
|
||||
public override Task MatchAsync(HttpContext httpContext, EndpointFeature feature)
|
||||
public override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context)
|
||||
{
|
||||
return CurrentMatcher.MatchAsync(httpContext, feature);
|
||||
return CurrentMatcher.MatchAsync(httpContext, context);
|
||||
}
|
||||
|
||||
private Matcher CreateMatcher(IReadOnlyList<Endpoint> endpoints)
|
||||
|
|
|
|||
|
|
@ -6,14 +6,13 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
|
||||
namespace Microsoft.AspNetCore.Routing.Matching
|
||||
{
|
||||
internal class DefaultEndpointSelector : EndpointSelector
|
||||
{
|
||||
private readonly IEndpointSelectorPolicy[] _selectorPolicies;
|
||||
|
||||
|
||||
public DefaultEndpointSelector(IEnumerable<MatcherPolicy> matcherPolicies)
|
||||
{
|
||||
if (matcherPolicies == null)
|
||||
|
|
@ -24,28 +23,88 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
_selectorPolicies = matcherPolicies.OrderBy(p => p.Order).OfType<IEndpointSelectorPolicy>().ToArray();
|
||||
}
|
||||
|
||||
public override async Task SelectAsync(
|
||||
public override Task SelectAsync(
|
||||
HttpContext httpContext,
|
||||
EndpointFeature feature,
|
||||
EndpointSelectorContext context,
|
||||
CandidateSet candidateSet)
|
||||
{
|
||||
if (httpContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(httpContext));
|
||||
}
|
||||
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (candidateSet == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(candidateSet));
|
||||
}
|
||||
|
||||
if (_selectorPolicies.Length > 0)
|
||||
{
|
||||
// Slow path: we need async to run policies
|
||||
return SelectAsyncSlow(httpContext, context, candidateSet);
|
||||
}
|
||||
|
||||
// Fast path: We can specialize for trivial numbers of candidates since there can
|
||||
// be no ambiguities and we don't need to run policies.
|
||||
switch (candidateSet.Count)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
{
|
||||
if (candidateSet.IsValidCandidate(0))
|
||||
{
|
||||
ref var state = ref candidateSet[0];
|
||||
context.Endpoint = state.Endpoint;
|
||||
context.RouteValues = state.Values;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
// Slow path: There's more than one candidate (to say nothing of validity) so we
|
||||
// have to process for ambiguities.
|
||||
ProcessFinalCandidates(httpContext, context, candidateSet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task SelectAsyncSlow(
|
||||
HttpContext httpContext,
|
||||
EndpointSelectorContext context,
|
||||
CandidateSet candidateSet)
|
||||
{
|
||||
var selectorPolicies = _selectorPolicies;
|
||||
for (var i = 0; i < _selectorPolicies.Length; i++)
|
||||
for (var i = 0; i < selectorPolicies.Length; i++)
|
||||
{
|
||||
await selectorPolicies[i].ApplyAsync(httpContext, feature, candidateSet);
|
||||
if (feature.Endpoint != null)
|
||||
await selectorPolicies[i].ApplyAsync(httpContext, context, candidateSet);
|
||||
if (context.Endpoint != null)
|
||||
{
|
||||
// This is a short circuit, the selector chose an endpoint.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ProcessFinalCandidates(httpContext, feature, candidateSet);
|
||||
ProcessFinalCandidates(httpContext, context, candidateSet);
|
||||
}
|
||||
|
||||
private static void ProcessFinalCandidates(
|
||||
HttpContext httpContext,
|
||||
EndpointFeature feature,
|
||||
EndpointSelectorContext context,
|
||||
CandidateSet candidateSet)
|
||||
{
|
||||
Endpoint endpoint = null;
|
||||
|
|
@ -54,8 +113,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
for (var i = 0; i < candidateSet.Count; i++)
|
||||
{
|
||||
ref var state = ref candidateSet[i];
|
||||
|
||||
var isValid = state.IsValidCandidate;
|
||||
var isValid = candidateSet.IsValidCandidate(i);
|
||||
if (isValid && foundScore == null)
|
||||
{
|
||||
// This is the first match we've seen - speculatively assign it.
|
||||
|
|
@ -87,8 +145,8 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
|
||||
if (endpoint != null)
|
||||
{
|
||||
feature.Endpoint = endpoint;
|
||||
feature.RouteValues = values;
|
||||
context.Endpoint = endpoint;
|
||||
context.RouteValues = values;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -99,10 +157,9 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var matches = new List<Endpoint>();
|
||||
for (var i = 0; i < candidates.Count; i++)
|
||||
{
|
||||
ref var state = ref candidates[i];
|
||||
if (state.IsValidCandidate)
|
||||
if (candidates.IsValidCandidate(i))
|
||||
{
|
||||
matches.Add(state.Endpoint);
|
||||
matches.Add(candidates[i].Endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Routing.Patterns;
|
||||
|
||||
namespace Microsoft.AspNetCore.Routing.Matching
|
||||
|
|
@ -23,16 +22,16 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
_maxSegmentCount = maxSegmentCount;
|
||||
}
|
||||
|
||||
public sealed override Task MatchAsync(HttpContext httpContext, EndpointFeature feature)
|
||||
public sealed override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context)
|
||||
{
|
||||
if (httpContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(httpContext));
|
||||
}
|
||||
|
||||
if (feature == null)
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(feature));
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
// The sequence of actions we take is optimized to avoid doing expensive work
|
||||
|
|
@ -118,21 +117,26 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
// Now that we have the route values, we need to process complex segments.
|
||||
// Complex segments go through an old API that requires a fully-materialized
|
||||
// route value dictionary.
|
||||
var isMatch = true;
|
||||
if ((flags & Candidate.CandidateFlags.HasComplexSegments) != 0)
|
||||
{
|
||||
isMatch &= ProcessComplexSegments(candidate.ComplexSegments, path, segments, values);
|
||||
if (!ProcessComplexSegments(candidate.ComplexSegments, path, segments, values))
|
||||
{
|
||||
candidateSet.SetValidity(i, false);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & Candidate.CandidateFlags.HasConstraints) != 0)
|
||||
{
|
||||
isMatch &= ProcessConstraints(candidate.Constraints, httpContext, values);
|
||||
if (!ProcessConstraints(candidate.Constraints, httpContext, values))
|
||||
{
|
||||
candidateSet.SetValidity(i, false);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
state.IsValidCandidate = isMatch;
|
||||
}
|
||||
|
||||
return _selector.SelectAsync(httpContext, feature, candidateSet);
|
||||
return _selector.SelectAsync(httpContext, context, candidateSet);
|
||||
}
|
||||
|
||||
internal Candidate[] FindCandidateSet(
|
||||
|
|
|
|||
|
|
@ -18,16 +18,16 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
/// Asynchronously selects an <see cref="Endpoint"/> from the <see cref="CandidateSet"/>.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
|
||||
/// <param name="feature">The <see cref="IEndpointFeature"/> associated with the current request.</param>
|
||||
/// <param name="context">The <see cref="EndpointSelectorContext"/> associated with the current request.</param>
|
||||
/// <param name="candidates">The <see cref="CandidateSet"/>.</param>
|
||||
/// <returns>A <see cref="Task"/> that completes asynchronously once endpoint selection is complete.</returns>
|
||||
/// <remarks>
|
||||
/// An <see cref="EndpointSelector"/> should assign the <see cref="EndpointFeature.Endpoint"/>
|
||||
/// and <see cref="EndpointFeature.RouteValues"/> properties once an endpoint is selected.
|
||||
/// An <see cref="EndpointSelector"/> should assign the <see cref="EndpointSelectorContext.Endpoint"/>
|
||||
/// and <see cref="EndpointSelectorContext.RouteValues"/> properties once an endpoint is selected.
|
||||
/// </remarks>
|
||||
public abstract Task SelectAsync(
|
||||
HttpContext httpContext,
|
||||
EndpointFeature feature,
|
||||
EndpointSelectorContext context,
|
||||
CandidateSet candidates);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,21 +20,21 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
/// <param name="httpContext">
|
||||
/// The <see cref="HttpContext"/> associated with the current request.
|
||||
/// </param>
|
||||
/// <param name="feature">
|
||||
/// The <see cref="EndpointFeature"/> associated with the current request.
|
||||
/// <param name="context">
|
||||
/// The <see cref="EndpointSelectorContext"/> associated with the current request.
|
||||
/// </param>
|
||||
/// <param name="candidates">The <see cref="CandidateSet"/>.</param>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Implementations of <see cref="IEndpointSelectorPolicy"/> should implement this method
|
||||
/// and filter the set of candidates in the <paramref name="candidates"/> by setting
|
||||
/// <see cref="CandidateState.IsValidCandidate"/> to <c>false</c> where desired.
|
||||
/// <see cref="CandidateSet.SetValidity(int, bool)"/> to <c>false</c> where desired.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// To signal an error condition, set <see cref="EndpointFeature.Endpoint"/> to an
|
||||
/// To signal an error condition, set <see cref="EndpointSelectorContext.Endpoint"/> to an
|
||||
/// <see cref="Endpoint"/> value that will produce the desired error when executed.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
Task ApplyAsync(HttpContext httpContext, EndpointFeature feature, CandidateSet candidates);
|
||||
Task ApplyAsync(HttpContext httpContext, EndpointSelectorContext context, CandidateSet candidates);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
/// Attempts to asynchronously select an <see cref="Endpoint"/> for the current request.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
|
||||
/// <param name="feature">
|
||||
/// <param name="context">
|
||||
/// The <see cref="IEndpointFeature"/> associated with the current request. The
|
||||
/// <see cref="EndpointFeature"/> will be mutated to contain the result of the operation.</param>
|
||||
/// <see cref="EndpointSelectorContext"/> 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>
|
||||
public abstract Task MatchAsync(HttpContext httpContext, EndpointFeature feature);
|
||||
public abstract Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.RequestServices = new ServiceProvider();
|
||||
|
||||
httpContext.Features.Set<IEndpointFeature>(new EndpointFeature()
|
||||
httpContext.Features.Set<IEndpointFeature>(new EndpointSelectorContext()
|
||||
{
|
||||
Endpoint = null,
|
||||
});
|
||||
|
|
@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
httpContext.Features.Set<IEndpointFeature>(new EndpointFeature()
|
||||
httpContext.Features.Set<IEndpointFeature>(new EndpointSelectorContext()
|
||||
{
|
||||
Endpoint = new Endpoint(endpointFunc, EndpointMetadataCollection.Empty, "Test"),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -105,11 +105,11 @@ namespace Microsoft.AspNetCore.Routing
|
|||
|
||||
private HttpContext CreateHttpContext()
|
||||
{
|
||||
var feature = new EndpointFeature();
|
||||
var context = new EndpointSelectorContext();
|
||||
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Features.Set<IEndpointFeature>(feature);
|
||||
httpContext.Features.Set<IRouteValuesFeature>(feature);
|
||||
httpContext.Features.Set<IEndpointFeature>(context);
|
||||
httpContext.Features.Set<IRouteValuesFeature>(context);
|
||||
|
||||
httpContext.RequestServices = new TestServiceProvider();
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
public class EndpointFeatureTest
|
||||
public class EndpointSelectorContextTest
|
||||
{
|
||||
[Fact]
|
||||
public void RouteData_CanIntializeDataTokens_WithMetadata()
|
||||
|
|
@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
// Arrange
|
||||
var expected = new RouteValueDictionary(new { foo = 17, bar = "hello", });
|
||||
|
||||
var feature = new EndpointFeature()
|
||||
var context = new EndpointSelectorContext()
|
||||
{
|
||||
Endpoint = new RouteEndpoint(
|
||||
TestConstants.EmptyRequestDelegate,
|
||||
|
|
@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
};
|
||||
|
||||
// Act
|
||||
var routeData = ((IRoutingFeature)feature).RouteData;
|
||||
var routeData = ((IRoutingFeature)context).RouteData;
|
||||
|
||||
// Assert
|
||||
Assert.NotSame(expected, routeData.DataTokens);
|
||||
|
|
@ -39,7 +39,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
public void RouteData_DataTokensIsEmpty_WithoutMetadata()
|
||||
{
|
||||
// Arrange
|
||||
var feature = new EndpointFeature()
|
||||
var context = new EndpointSelectorContext()
|
||||
{
|
||||
Endpoint = new RouteEndpoint(
|
||||
TestConstants.EmptyRequestDelegate,
|
||||
|
|
@ -50,7 +50,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
};
|
||||
|
||||
// Act
|
||||
var routeData = ((IRoutingFeature)feature).RouteData;
|
||||
var routeData = ((IRoutingFeature)context).RouteData;
|
||||
|
||||
// Assert
|
||||
Assert.Empty(routeData.DataTokens);
|
||||
|
|
@ -26,12 +26,12 @@ namespace Microsoft.AspNetCore.Routing
|
|||
|
||||
var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2);
|
||||
|
||||
var feature = new EndpointFeature()
|
||||
var context = new EndpointSelectorContext()
|
||||
{
|
||||
RouteValues = new RouteValueDictionary(new { p = "5", })
|
||||
};
|
||||
var httpContext = CreateHttpContext();
|
||||
httpContext.Features.Set<IRouteValuesFeature>(feature);
|
||||
httpContext.Features.Set<IRouteValuesFeature>(context);
|
||||
httpContext.Request.PathBase = new PathString("/Foo/Bar?encodeme?");
|
||||
|
||||
var values = new { query = "some?query", };
|
||||
|
|
|
|||
|
|
@ -32,12 +32,12 @@ namespace Microsoft.AspNetCore.Routing
|
|||
|
||||
var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2);
|
||||
|
||||
var feature = new EndpointFeature()
|
||||
var context = new EndpointSelectorContext()
|
||||
{
|
||||
RouteValues = new RouteValueDictionary(new { action = "Index", })
|
||||
};
|
||||
var httpContext = CreateHttpContext();
|
||||
httpContext.Features.Set<IRouteValuesFeature>(feature);
|
||||
httpContext.Features.Set<IRouteValuesFeature>(context);
|
||||
httpContext.Request.PathBase = new PathString("/Foo/Bar?encodeme?");
|
||||
|
||||
// Act
|
||||
|
|
|
|||
|
|
@ -18,13 +18,13 @@ namespace Microsoft.AspNetCore.Routing
|
|||
{
|
||||
var httpContext = new DefaultHttpContext();
|
||||
|
||||
var feature = new EndpointFeature
|
||||
var context = new EndpointSelectorContext
|
||||
{
|
||||
RouteValues = new RouteValueDictionary(ambientValues)
|
||||
};
|
||||
|
||||
httpContext.Features.Set<IEndpointFeature>(feature);
|
||||
httpContext.Features.Set<IRouteValuesFeature>(feature);
|
||||
httpContext.Features.Set<IEndpointFeature>(context);
|
||||
httpContext.Features.Set<IRouteValuesFeature>(context);
|
||||
return httpContext;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,16 +20,16 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
Matchers = matchers;
|
||||
}
|
||||
|
||||
public override Task MatchAsync(HttpContext httpContext, EndpointFeature feature)
|
||||
public override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context)
|
||||
{
|
||||
if (httpContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(httpContext));
|
||||
}
|
||||
|
||||
if (feature == null)
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(feature));
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var path = httpContext.Request.Path.Value;
|
||||
|
|
@ -37,8 +37,8 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
if (Matchers[i].TryMatch(path))
|
||||
{
|
||||
feature.Endpoint = Matchers[i].Endpoint;
|
||||
feature.RouteValues = new RouteValueDictionary();
|
||||
context.Endpoint = Matchers[i].Endpoint;
|
||||
context.RouteValues = new RouteValueDictionary();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -121,12 +121,12 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
return Array.Empty<Candidate>();
|
||||
}
|
||||
|
||||
public override Task MatchAsync(HttpContext httpContext, EndpointFeature feature)
|
||||
public override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context)
|
||||
{
|
||||
if (TryMatch(httpContext.Request.Path.Value))
|
||||
{
|
||||
feature.Endpoint = Endpoint;
|
||||
feature.RouteValues = new RouteValueDictionary();
|
||||
context.Endpoint = Endpoint;
|
||||
context.RouteValues = new RouteValueDictionary();
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
[InlineData(4)]
|
||||
[InlineData(5)] // this is the break-point where we start to use a list.
|
||||
[InlineData(6)]
|
||||
[InlineData(31)]
|
||||
[InlineData(32)] // this is the break point where we use a BitArray
|
||||
[InlineData(33)]
|
||||
public void Create_CreatesCandidateSet(int count)
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -41,10 +44,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
for (var i = 0; i < candidateSet.Count; i++)
|
||||
{
|
||||
ref var state = ref candidateSet[i];
|
||||
Assert.True(state.IsValidCandidate);
|
||||
Assert.True(candidateSet.IsValidCandidate(i));
|
||||
Assert.Same(endpoints[i], state.Endpoint);
|
||||
Assert.Equal(candidates[i].Score, state.Score);
|
||||
Assert.Null(state.Values);
|
||||
|
||||
candidateSet.SetValidity(i, false);
|
||||
Assert.False(candidateSet.IsValidCandidate(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -58,6 +64,9 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
[InlineData(4)]
|
||||
[InlineData(5)] // this is the break-point where we start to use a list.
|
||||
[InlineData(6)]
|
||||
[InlineData(31)]
|
||||
[InlineData(32)] // this is the break point where we use a BitArray
|
||||
[InlineData(33)]
|
||||
public void Create_CreatesCandidateSet_TestConstructor(int count)
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -67,17 +76,30 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
endpoints[i] = CreateEndpoint($"/{i}");
|
||||
}
|
||||
|
||||
var values = new RouteValueDictionary[count];
|
||||
for (var i = 0; i < endpoints.Length; i++)
|
||||
{
|
||||
values[i] = new RouteValueDictionary()
|
||||
{
|
||||
{ "i", i }
|
||||
};
|
||||
}
|
||||
|
||||
// Act
|
||||
var candidateSet = new CandidateSet(endpoints, Enumerable.Range(0, count).ToArray());
|
||||
var candidateSet = new CandidateSet(endpoints, values, Enumerable.Range(0, count).ToArray());
|
||||
|
||||
// Assert
|
||||
for (var i = 0; i < candidateSet.Count; i++)
|
||||
{
|
||||
ref var state = ref candidateSet[i];
|
||||
Assert.True(state.IsValidCandidate);
|
||||
Assert.True(candidateSet.IsValidCandidate(i));
|
||||
Assert.Same(endpoints[i], state.Endpoint);
|
||||
Assert.Equal(i, state.Score);
|
||||
Assert.Null(state.Values);
|
||||
Assert.NotNull(state.Values);
|
||||
Assert.Equal(i, state.Values["i"]);
|
||||
|
||||
candidateSet.SetValidity(i, false);
|
||||
Assert.False(candidateSet.IsValidCandidate(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
public IReadOnlyList<RouteEndpoint> Endpoints { get; set; }
|
||||
|
||||
public override Task MatchAsync(HttpContext httpContext, EndpointFeature feature)
|
||||
public override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,14 +21,14 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var scores = new int[] { };
|
||||
var candidateSet = CreateCandidateSet(endpoints, scores);
|
||||
|
||||
var (httpContext, feature) = CreateContext();
|
||||
var (httpContext, context) = CreateContext();
|
||||
var selector = CreateSelector();
|
||||
|
||||
// Act
|
||||
await selector.SelectAsync(httpContext, feature, candidateSet);
|
||||
await selector.SelectAsync(httpContext, context, candidateSet);
|
||||
|
||||
// Assert
|
||||
Assert.Null(feature.Endpoint);
|
||||
Assert.Null(context.Endpoint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -40,16 +40,16 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var candidateSet = CreateCandidateSet(endpoints, scores);
|
||||
|
||||
candidateSet[0].Values = new RouteValueDictionary();
|
||||
candidateSet[0].IsValidCandidate = false;
|
||||
candidateSet.SetValidity(0, false);
|
||||
|
||||
var (httpContext, feature) = CreateContext();
|
||||
var (httpContext, context) = CreateContext();
|
||||
var selector = CreateSelector();
|
||||
|
||||
// Act
|
||||
await selector.SelectAsync(httpContext, feature, candidateSet);
|
||||
await selector.SelectAsync(httpContext, context, candidateSet);
|
||||
|
||||
// Assert
|
||||
Assert.Null(feature.Endpoint);
|
||||
Assert.Null(context.Endpoint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -61,16 +61,16 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var candidateSet = CreateCandidateSet(endpoints, scores);
|
||||
|
||||
candidateSet[0].Values = new RouteValueDictionary();
|
||||
candidateSet[0].IsValidCandidate = true;
|
||||
candidateSet.SetValidity(0, true);
|
||||
|
||||
var (httpContext, feature) = CreateContext();
|
||||
var (httpContext, context) = CreateContext();
|
||||
var selector = CreateSelector();
|
||||
|
||||
// Act
|
||||
await selector.SelectAsync(httpContext, feature, candidateSet);
|
||||
await selector.SelectAsync(httpContext, context, candidateSet);
|
||||
|
||||
// Assert
|
||||
Assert.Same(endpoints[0], feature.Endpoint);
|
||||
Assert.Same(endpoints[0], context.Endpoint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -81,17 +81,17 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var scores = new int[] { 0, 0 };
|
||||
var candidateSet = CreateCandidateSet(endpoints, scores);
|
||||
|
||||
candidateSet[0].IsValidCandidate = false;
|
||||
candidateSet[1].IsValidCandidate = true;
|
||||
candidateSet.SetValidity(0, false);
|
||||
candidateSet.SetValidity(1, true);
|
||||
|
||||
var (httpContext, feature) = CreateContext();
|
||||
var (httpContext, context) = CreateContext();
|
||||
var selector = CreateSelector();
|
||||
|
||||
// Act
|
||||
await selector.SelectAsync(httpContext, feature, candidateSet);
|
||||
await selector.SelectAsync(httpContext, context, candidateSet);
|
||||
|
||||
// Assert
|
||||
Assert.Same(endpoints[1], feature.Endpoint);
|
||||
Assert.Same(endpoints[1], context.Endpoint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -102,18 +102,18 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var scores = new int[] { 0, 0, 1 };
|
||||
var candidateSet = CreateCandidateSet(endpoints, scores);
|
||||
|
||||
candidateSet[0].IsValidCandidate = false;
|
||||
candidateSet[1].IsValidCandidate = true;
|
||||
candidateSet[2].IsValidCandidate = true;
|
||||
candidateSet.SetValidity(0, false);
|
||||
candidateSet.SetValidity(1, true);
|
||||
candidateSet.SetValidity(2, true);
|
||||
|
||||
var (httpContext, feature) = CreateContext();
|
||||
var (httpContext, context) = CreateContext();
|
||||
var selector = CreateSelector();
|
||||
|
||||
// Act
|
||||
await selector.SelectAsync(httpContext, feature, candidateSet);
|
||||
await selector.SelectAsync(httpContext, context, candidateSet);
|
||||
|
||||
// Assert
|
||||
Assert.Same(endpoints[1], feature.Endpoint);
|
||||
Assert.Same(endpoints[1], context.Endpoint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -131,20 +131,20 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var scores = new int[] { 0, 1, 2, 3, 4 };
|
||||
var candidateSet = CreateCandidateSet(endpoints, scores);
|
||||
|
||||
candidateSet[0].IsValidCandidate = false;
|
||||
candidateSet[1].IsValidCandidate = false;
|
||||
candidateSet[2].IsValidCandidate = false;
|
||||
candidateSet[3].IsValidCandidate = false;
|
||||
candidateSet[4].IsValidCandidate = true;
|
||||
candidateSet.SetValidity(0, false);
|
||||
candidateSet.SetValidity(1, false);
|
||||
candidateSet.SetValidity(2, false);
|
||||
candidateSet.SetValidity(3, false);
|
||||
candidateSet.SetValidity(4, true);
|
||||
|
||||
var (httpContext, feature) = CreateContext();
|
||||
var (httpContext, context) = CreateContext();
|
||||
var selector = CreateSelector();
|
||||
|
||||
// Act
|
||||
await selector.SelectAsync(httpContext, feature, candidateSet);
|
||||
await selector.SelectAsync(httpContext, context, candidateSet);
|
||||
|
||||
// Assert
|
||||
Assert.Same(endpoints[4], feature.Endpoint);
|
||||
Assert.Same(endpoints[4], context.Endpoint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -155,15 +155,15 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var scores = new int[] { 0, 1, 1 };
|
||||
var candidateSet = CreateCandidateSet(endpoints, scores);
|
||||
|
||||
candidateSet[0].IsValidCandidate = false;
|
||||
candidateSet[1].IsValidCandidate = true;
|
||||
candidateSet[2].IsValidCandidate = true;
|
||||
candidateSet.SetValidity(0, false);
|
||||
candidateSet.SetValidity(1, true);
|
||||
candidateSet.SetValidity(2, true);
|
||||
|
||||
var (httpContext, feature) = CreateContext();
|
||||
var (httpContext, context) = CreateContext();
|
||||
var selector = CreateSelector();
|
||||
|
||||
// Act
|
||||
var ex = await Assert.ThrowsAsync<AmbiguousMatchException>(() => selector.SelectAsync(httpContext, feature, candidateSet));
|
||||
var ex = await Assert.ThrowsAsync<AmbiguousMatchException>(() => selector.SelectAsync(httpContext, context, candidateSet));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(
|
||||
|
|
@ -171,7 +171,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
|
||||
test: /test2
|
||||
test: /test3", ex.Message);
|
||||
Assert.Null(feature.Endpoint);
|
||||
Assert.Null(context.Endpoint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -185,25 +185,25 @@ test: /test3", ex.Message);
|
|||
var policy = new Mock<MatcherPolicy>();
|
||||
policy
|
||||
.As<IEndpointSelectorPolicy>()
|
||||
.Setup(p => p.ApplyAsync(It.IsAny<HttpContext>(), It.IsAny<EndpointFeature>(), It.IsAny<CandidateSet>()))
|
||||
.Returns<HttpContext, EndpointFeature, CandidateSet>((c, f, cs) =>
|
||||
.Setup(p => p.ApplyAsync(It.IsAny<HttpContext>(), It.IsAny<EndpointSelectorContext>(), It.IsAny<CandidateSet>()))
|
||||
.Returns<HttpContext, EndpointSelectorContext, CandidateSet>((c, f, cs) =>
|
||||
{
|
||||
cs[1].IsValidCandidate = false;
|
||||
cs.SetValidity(1, false);
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
candidateSet[0].IsValidCandidate = false;
|
||||
candidateSet[1].IsValidCandidate = true;
|
||||
candidateSet[2].IsValidCandidate = true;
|
||||
candidateSet.SetValidity(0, false);
|
||||
candidateSet.SetValidity(1, true);
|
||||
candidateSet.SetValidity(2, true);
|
||||
|
||||
var (httpContext, feature) = CreateContext();
|
||||
var (httpContext, context) = CreateContext();
|
||||
var selector = CreateSelector(policy.Object);
|
||||
|
||||
// Act
|
||||
await selector.SelectAsync(httpContext, feature, candidateSet);
|
||||
await selector.SelectAsync(httpContext, context, candidateSet);
|
||||
|
||||
// Assert
|
||||
Assert.Same(endpoints[2], feature.Endpoint);
|
||||
Assert.Same(endpoints[2], context.Endpoint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -217,8 +217,8 @@ test: /test3", ex.Message);
|
|||
var policy1 = new Mock<MatcherPolicy>();
|
||||
policy1
|
||||
.As<IEndpointSelectorPolicy>()
|
||||
.Setup(p => p.ApplyAsync(It.IsAny<HttpContext>(), It.IsAny<EndpointFeature>(), It.IsAny<CandidateSet>()))
|
||||
.Returns<HttpContext, EndpointFeature, CandidateSet>((c, f, cs) =>
|
||||
.Setup(p => p.ApplyAsync(It.IsAny<HttpContext>(), It.IsAny<EndpointSelectorContext>(), It.IsAny<CandidateSet>()))
|
||||
.Returns<HttpContext, EndpointSelectorContext, CandidateSet>((c, f, cs) =>
|
||||
{
|
||||
f.Endpoint = cs[0].Endpoint;
|
||||
return Task.CompletedTask;
|
||||
|
|
@ -231,31 +231,31 @@ test: /test3", ex.Message);
|
|||
.Returns(1000);
|
||||
policy2
|
||||
.As<IEndpointSelectorPolicy>()
|
||||
.Setup(p => p.ApplyAsync(It.IsAny<HttpContext>(), It.IsAny<EndpointFeature>(), It.IsAny<CandidateSet>()))
|
||||
.Setup(p => p.ApplyAsync(It.IsAny<HttpContext>(), It.IsAny<EndpointSelectorContext>(), It.IsAny<CandidateSet>()))
|
||||
.Throws(new InvalidOperationException());
|
||||
|
||||
candidateSet[0].IsValidCandidate = false;
|
||||
candidateSet[1].IsValidCandidate = true;
|
||||
candidateSet[2].IsValidCandidate = true;
|
||||
candidateSet.SetValidity(0, false);
|
||||
candidateSet.SetValidity(1, true);
|
||||
candidateSet.SetValidity(2, true);
|
||||
|
||||
var (httpContext, feature) = CreateContext();
|
||||
var (httpContext, context) = CreateContext();
|
||||
var selector = CreateSelector(policy1.Object, policy2.Object);
|
||||
|
||||
// Act
|
||||
await selector.SelectAsync(httpContext, feature, candidateSet);
|
||||
await selector.SelectAsync(httpContext, context, candidateSet);
|
||||
|
||||
// Assert
|
||||
Assert.Same(endpoints[0], feature.Endpoint);
|
||||
Assert.Same(endpoints[0], context.Endpoint);
|
||||
}
|
||||
|
||||
private static (HttpContext httpContext, EndpointFeature feature) CreateContext()
|
||||
private static (HttpContext httpContext, EndpointSelectorContext context) CreateContext()
|
||||
{
|
||||
var feature = new EndpointFeature();
|
||||
var context = new EndpointSelectorContext();
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Features.Set<IEndpointFeature>(feature);
|
||||
httpContext.Features.Set<IRouteValuesFeature>(feature);
|
||||
httpContext.Features.Set<IEndpointFeature>(context);
|
||||
httpContext.Features.Set<IRouteValuesFeature>(context);
|
||||
|
||||
return (httpContext, feature);
|
||||
return (httpContext, context);
|
||||
}
|
||||
|
||||
private static RouteEndpoint CreateEndpoint(string template)
|
||||
|
|
@ -270,7 +270,7 @@ test: /test3", ex.Message);
|
|||
|
||||
private static CandidateSet CreateCandidateSet(RouteEndpoint[] endpoints, int[] scores)
|
||||
{
|
||||
return new CandidateSet(endpoints, scores);
|
||||
return new CandidateSet(endpoints, new RouteValueDictionary[endpoints.Length], scores);
|
||||
}
|
||||
|
||||
private static DefaultEndpointSelector CreateSelector(params MatcherPolicy[] policies)
|
||||
|
|
|
|||
|
|
@ -14,13 +14,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher(template);
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint, keys, values);
|
||||
}
|
||||
|
||||
internal override Matcher CreateMatcher(params RouteEndpoint[] endpoints)
|
||||
|
|
|
|||
|
|
@ -55,14 +55,14 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
|
||||
var matcher = CreateDfaMatcher(endpointDataSource);
|
||||
|
||||
var (httpContext, endpointFeature) = CreateHttpContext();
|
||||
var (httpContext, context) = CreateContext();
|
||||
httpContext.Request.Path = "/1";
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, endpointFeature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(endpointFeature.Endpoint);
|
||||
Assert.NotNull(context.Endpoint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -76,7 +76,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
|
||||
var matcher = CreateDfaMatcher(endpointDataSource);
|
||||
|
||||
var (httpContext, endpointFeature) = CreateHttpContext();
|
||||
var (httpContext, endpointFeature) = CreateContext();
|
||||
httpContext.Request.Path = "/One";
|
||||
|
||||
// Act
|
||||
|
|
@ -101,7 +101,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
|
||||
var matcher = CreateDfaMatcher(endpointDataSource);
|
||||
|
||||
var (httpContext, endpointFeature) = CreateHttpContext();
|
||||
var (httpContext, endpointFeature) = CreateContext();
|
||||
httpContext.Request.Path = "/Teams";
|
||||
|
||||
// Act
|
||||
|
|
@ -120,18 +120,18 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
|
||||
var endpointSelector = new Mock<EndpointSelector>();
|
||||
endpointSelector
|
||||
.Setup(s => s.SelectAsync(It.IsAny<HttpContext>(), It.IsAny<EndpointFeature>(), It.IsAny<CandidateSet>()))
|
||||
.Setup(s => s.SelectAsync(It.IsAny<HttpContext>(), It.IsAny<EndpointSelectorContext>(), It.IsAny<CandidateSet>()))
|
||||
.Callback<HttpContext, IEndpointFeature, CandidateSet>((c, f, cs) =>
|
||||
{
|
||||
Assert.Equal(2, cs.Count);
|
||||
|
||||
Assert.Same(endpoint1, cs[0].Endpoint);
|
||||
Assert.True(cs[0].IsValidCandidate);
|
||||
Assert.True(cs.IsValidCandidate(0));
|
||||
Assert.Equal(0, cs[0].Score);
|
||||
Assert.Empty(cs[0].Values);
|
||||
|
||||
Assert.Same(endpoint2, cs[1].Endpoint);
|
||||
Assert.True(cs[1].IsValidCandidate);
|
||||
Assert.True(cs.IsValidCandidate(1));
|
||||
Assert.Equal(1, cs[1].Score);
|
||||
Assert.Empty(cs[1].Values);
|
||||
|
||||
|
|
@ -147,7 +147,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
|
||||
var matcher = CreateDfaMatcher(endpointDataSource, endpointSelector.Object);
|
||||
|
||||
var (httpContext, endpointFeature) = CreateHttpContext();
|
||||
var (httpContext, endpointFeature) = CreateContext();
|
||||
httpContext.Request.Path = "/Teams";
|
||||
|
||||
// Act
|
||||
|
|
@ -157,15 +157,15 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
Assert.Equal(endpoint2, endpointFeature.Endpoint);
|
||||
}
|
||||
|
||||
private (HttpContext httpContext, EndpointFeature feature) CreateHttpContext()
|
||||
private (HttpContext httpContext, EndpointSelectorContext context) CreateContext()
|
||||
{
|
||||
var feature = new EndpointFeature();
|
||||
var context = new EndpointSelectorContext();
|
||||
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Features.Set<IEndpointFeature>(feature);
|
||||
httpContext.Features.Set<IRouteValuesFeature>(feature);
|
||||
httpContext.Features.Set<IEndpointFeature>(context);
|
||||
httpContext.Features.Set<IRouteValuesFeature>(context);
|
||||
|
||||
return (httpContext, feature);
|
||||
return (httpContext, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,13 +28,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher(template);
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint, keys, values);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -43,13 +43,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
// Arrange
|
||||
var endpoint = CreateEndpoint("/a/{b}/{c}", new { b = "17", c = "18", });
|
||||
var matcher = CreateMatcher(endpoint);
|
||||
var (httpContext, feature) = CreateContext("/a");
|
||||
var (httpContext, context) = CreateContext("/a");
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint, new { b = "17", c = "18", });
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint, new { b = "17", c = "18", });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -58,13 +58,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
// Arrange
|
||||
var endpoint = CreateEndpoint("/a/{b}/{c}", new { b = "17", c = "18", d = "19" });
|
||||
var matcher = CreateMatcher(endpoint);
|
||||
var (httpContext, feature) = CreateContext("/a");
|
||||
var (httpContext, context) = CreateContext("/a");
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint, new { b = "17", c = "18", d = "19" });
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint, new { b = "17", c = "18", d = "19" });
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -83,13 +83,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher(template);
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertNotMatch(feature, httpContext);
|
||||
MatcherAssert.AssertNotMatch(context, httpContext);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -115,13 +115,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher(template);
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint, keys, values);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -138,13 +138,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher(template);
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertNotMatch(feature, httpContext);
|
||||
MatcherAssert.AssertNotMatch(context, httpContext);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -162,13 +162,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher(template);
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint, keys, values);
|
||||
}
|
||||
|
||||
// Historically catchall segments don't match an empty segment, but only if it's
|
||||
|
|
@ -182,13 +182,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher(template);
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertNotMatch(feature, httpContext);
|
||||
MatcherAssert.AssertNotMatch(context, httpContext);
|
||||
|
||||
// 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.
|
||||
|
|
@ -217,13 +217,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher(template);
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint, keys, values);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -237,13 +237,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher(template);
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertNotMatch(feature, httpContext);
|
||||
MatcherAssert.AssertNotMatch(context, httpContext);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -266,13 +266,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher(template);
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint, keys, values);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -292,13 +292,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher(template);
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertNotMatch(feature, httpContext);
|
||||
MatcherAssert.AssertNotMatch(context, httpContext);
|
||||
}
|
||||
|
||||
// Most of are copied from old routing tests that date back to the VS 2010 era. Enjoy!
|
||||
|
|
@ -317,13 +317,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher(template);
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint, keys, values);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -347,13 +347,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
|
||||
// Arrange
|
||||
var matcher = CreateMatcher(other, expected);
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, expected, ignoreValues: true);
|
||||
MatcherAssert.AssertMatch(context, httpContext, expected, ignoreValues: true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -381,13 +381,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
|
||||
// Arrange
|
||||
var matcher = CreateMatcher(other, expected);
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, expected, ignoreValues: true);
|
||||
MatcherAssert.AssertMatch(context, httpContext, expected, ignoreValues: true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -434,13 +434,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var expected = endpoints[Array.IndexOf(templates, expectedTemplate)];
|
||||
|
||||
var matcher = CreateMatcher(endpoints);
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, expected, ignoreValues: true);
|
||||
MatcherAssert.AssertMatch(context, httpContext, expected, ignoreValues: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -23,13 +23,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var endpoint = CreateEndpoint("/hello", httpMethods: new string[] { "GET", });
|
||||
|
||||
var matcher = CreateMatcher(endpoint);
|
||||
var (httpContext, feature) = CreateContext("/hello", "GET");
|
||||
var (httpContext, context) = CreateContext("/hello", "GET");
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -39,13 +39,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var endpoint = CreateEndpoint("/hello", httpMethods: new string[] { "GET", }, acceptCorsPreflight: true);
|
||||
|
||||
var matcher = CreateMatcher(endpoint);
|
||||
var (httpContext, feature) = CreateContext("/hello", "GET");
|
||||
var (httpContext, context) = CreateContext("/hello", "GET");
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -55,13 +55,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var endpoint = CreateEndpoint("/hello", httpMethods: new string[] { "GET", }, acceptCorsPreflight: true);
|
||||
|
||||
var matcher = CreateMatcher(endpoint);
|
||||
var (httpContext, feature) = CreateContext("/hello", "GET", corsPreflight: true);
|
||||
var (httpContext, context) = CreateContext("/hello", "GET", corsPreflight: true);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -72,14 +72,14 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var endpoint = CreateEndpoint("/hello", httpMethods: new string[] { "GET", }, acceptCorsPreflight: false);
|
||||
|
||||
var matcher = CreateMatcher(endpoint);
|
||||
var (httpContext, feature) = CreateContext("/hello", "GET", corsPreflight: true);
|
||||
var (httpContext, context) = CreateContext("/hello", "GET", corsPreflight: true);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
Assert.NotSame(endpoint, feature.Endpoint);
|
||||
Assert.Same(HttpMethodMatcherPolicy.Http405EndpointDisplayName, feature.Endpoint.DisplayName);
|
||||
Assert.NotSame(endpoint, context.Endpoint);
|
||||
Assert.Same(HttpMethodMatcherPolicy.Http405EndpointDisplayName, context.Endpoint.DisplayName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -89,13 +89,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var endpoint = CreateEndpoint("/hello", httpMethods: new string[] { "GeT", });
|
||||
|
||||
var matcher = CreateMatcher(endpoint);
|
||||
var (httpContext, feature) = CreateContext("/hello", "GET");
|
||||
var (httpContext, context) = CreateContext("/hello", "GET");
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -105,13 +105,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var endpoint = CreateEndpoint("/hello", httpMethods: new string[] { "GeT", }, acceptCorsPreflight: true);
|
||||
|
||||
var matcher = CreateMatcher(endpoint);
|
||||
var (httpContext, feature) = CreateContext("/hello", "GET", corsPreflight: true);
|
||||
var (httpContext, context) = CreateContext("/hello", "GET", corsPreflight: true);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -121,13 +121,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var endpoint = CreateEndpoint("/hello");
|
||||
|
||||
var matcher = CreateMatcher(endpoint);
|
||||
var (httpContext, feature) = CreateContext("/hello", "GET");
|
||||
var (httpContext, context) = CreateContext("/hello", "GET");
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -137,13 +137,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var endpoint = CreateEndpoint("/hello", acceptCorsPreflight: true);
|
||||
|
||||
var matcher = CreateMatcher(endpoint);
|
||||
var (httpContext, feature) = CreateContext("/hello", "GET", corsPreflight: true);
|
||||
var (httpContext, context) = CreateContext("/hello", "GET", corsPreflight: true);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint);
|
||||
}
|
||||
|
||||
[Fact] // This matches because the endpoint accepts OPTIONS
|
||||
|
|
@ -153,13 +153,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var endpoint = CreateEndpoint("/hello", acceptCorsPreflight: false);
|
||||
|
||||
var matcher = CreateMatcher(endpoint);
|
||||
var (httpContext, feature) = CreateContext("/hello", "GET", corsPreflight: true);
|
||||
var (httpContext, context) = CreateContext("/hello", "GET", corsPreflight: true);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -169,13 +169,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var endpoint = CreateEndpoint("/hello", httpMethods: new string[] { });
|
||||
|
||||
var matcher = CreateMatcher(endpoint);
|
||||
var (httpContext, feature) = CreateContext("/hello", "GET");
|
||||
var (httpContext, context) = CreateContext("/hello", "GET");
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint);
|
||||
}
|
||||
|
||||
[Fact] // When all of the candidates handles specific verbs, use a 405 endpoint
|
||||
|
|
@ -186,19 +186,19 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var endpoint2 = CreateEndpoint("/hello", httpMethods: new string[] { "DELETE" });
|
||||
|
||||
var matcher = CreateMatcher(endpoint1, endpoint2);
|
||||
var (httpContext, feature) = CreateContext("/hello", "POST");
|
||||
var (httpContext, context) = CreateContext("/hello", "POST");
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
Assert.NotSame(endpoint1, feature.Endpoint);
|
||||
Assert.NotSame(endpoint2, feature.Endpoint);
|
||||
Assert.NotSame(endpoint1, context.Endpoint);
|
||||
Assert.NotSame(endpoint2, context.Endpoint);
|
||||
|
||||
Assert.Same(HttpMethodMatcherPolicy.Http405EndpointDisplayName, feature.Endpoint.DisplayName);
|
||||
Assert.Same(HttpMethodMatcherPolicy.Http405EndpointDisplayName, context.Endpoint.DisplayName);
|
||||
|
||||
// Invoke the endpoint
|
||||
await feature.Endpoint.RequestDelegate(httpContext);
|
||||
await context.Endpoint.RequestDelegate(httpContext);
|
||||
Assert.Equal(405, httpContext.Response.StatusCode);
|
||||
Assert.Equal("DELETE, GET, PUT", httpContext.Response.Headers["Allow"]);
|
||||
}
|
||||
|
|
@ -211,13 +211,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var endpoint2 = CreateEndpoint("/hello", httpMethods: new string[] { "DELETE" });
|
||||
|
||||
var matcher = CreateMatcher(endpoint1, endpoint2);
|
||||
var (httpContext, feature) = CreateContext("/hello", "POST", corsPreflight: true);
|
||||
var (httpContext, context) = CreateContext("/hello", "POST", corsPreflight: true);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertNotMatch(feature, httpContext);
|
||||
MatcherAssert.AssertNotMatch(context, httpContext);
|
||||
}
|
||||
|
||||
[Fact] // When one of the candidates handles all verbs, dont use a 405 endpoint
|
||||
|
|
@ -228,13 +228,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var endpoint2 = CreateEndpoint("/hello", httpMethods: new string[] { "DELETE" });
|
||||
|
||||
var matcher = CreateMatcher(endpoint1, endpoint2);
|
||||
var (httpContext, feature) = CreateContext("/hello", "POST");
|
||||
var (httpContext, context) = CreateContext("/hello", "POST");
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertNotMatch(feature, httpContext);
|
||||
MatcherAssert.AssertNotMatch(context, httpContext);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -245,13 +245,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var endpoint2 = CreateEndpoint("/bar");
|
||||
|
||||
var matcher = CreateMatcher(endpoint1, endpoint2);
|
||||
var (httpContext, feature) = CreateContext("/hello", "GET");
|
||||
var (httpContext, context) = CreateContext("/hello", "GET");
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint1);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -262,13 +262,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var endpoint2 = CreateEndpoint("/bar", httpMethods: new string[] { });
|
||||
|
||||
var matcher = CreateMatcher(endpoint1, endpoint2);
|
||||
var (httpContext, feature) = CreateContext("/hello", "GET");
|
||||
var (httpContext, context) = CreateContext("/hello", "GET");
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint1);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint1);
|
||||
}
|
||||
|
||||
[Fact] // The non-http-method-specific endpoint is part of the same candidate set
|
||||
|
|
@ -279,13 +279,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var endpoint2 = CreateEndpoint("/{x}", httpMethods: new string[] { });
|
||||
|
||||
var matcher = CreateMatcher(endpoint1, endpoint2);
|
||||
var (httpContext, feature) = CreateContext("/hello", "POST");
|
||||
var (httpContext, context) = CreateContext("/hello", "POST");
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint2, ignoreValues: true);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint2, ignoreValues: true);
|
||||
}
|
||||
|
||||
private static Matcher CreateMatcher(params RouteEndpoint[] endpoints)
|
||||
|
|
@ -305,7 +305,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
return builder.Build();
|
||||
}
|
||||
|
||||
internal static (HttpContext httpContext, EndpointFeature feature) CreateContext(
|
||||
internal static (HttpContext httpContext, EndpointSelectorContext context) CreateContext(
|
||||
string path,
|
||||
string httpMethod,
|
||||
bool corsPreflight = false)
|
||||
|
|
@ -320,11 +320,11 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
httpContext.Request.Headers[AccessControlRequestMethod] = httpMethod;
|
||||
}
|
||||
|
||||
var feature = new EndpointFeature();
|
||||
httpContext.Features.Set<IEndpointFeature>(feature);
|
||||
httpContext.Features.Set<IRouteValuesFeature>(feature);
|
||||
var context = new EndpointSelectorContext();
|
||||
httpContext.Features.Set<IEndpointFeature>(context);
|
||||
httpContext.Features.Set<IRouteValuesFeature>(context);
|
||||
|
||||
return (httpContext, feature);
|
||||
return (httpContext, context);
|
||||
}
|
||||
internal static RouteEndpoint CreateEndpoint(
|
||||
string template,
|
||||
|
|
|
|||
|
|
@ -12,22 +12,22 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
internal static class MatcherAssert
|
||||
{
|
||||
public static void AssertMatch(EndpointFeature feature, HttpContext context, Endpoint expected)
|
||||
public static void AssertMatch(EndpointSelectorContext context, HttpContext httpContext, Endpoint expected)
|
||||
{
|
||||
AssertMatch(feature, context, expected, new RouteValueDictionary());
|
||||
AssertMatch(context, httpContext, expected, new RouteValueDictionary());
|
||||
}
|
||||
|
||||
public static void AssertMatch(EndpointFeature feature, HttpContext context, Endpoint expected, bool ignoreValues)
|
||||
public static void AssertMatch(EndpointSelectorContext context, HttpContext httpContext, Endpoint expected, bool ignoreValues)
|
||||
{
|
||||
AssertMatch(feature, context, expected, new RouteValueDictionary(), ignoreValues);
|
||||
AssertMatch(context, httpContext, expected, new RouteValueDictionary(), ignoreValues);
|
||||
}
|
||||
|
||||
public static void AssertMatch(EndpointFeature feature, HttpContext context, Endpoint expected, object values)
|
||||
public static void AssertMatch(EndpointSelectorContext context, HttpContext httpContext, Endpoint expected, object values)
|
||||
{
|
||||
AssertMatch(feature, context, expected, new RouteValueDictionary(values));
|
||||
AssertMatch(context, httpContext, expected, new RouteValueDictionary(values));
|
||||
}
|
||||
|
||||
public static void AssertMatch(EndpointFeature feature, HttpContext context, Endpoint expected, string[] keys, string[] values)
|
||||
public static void AssertMatch(EndpointSelectorContext context, HttpContext httpContext, Endpoint expected, string[] keys, string[] values)
|
||||
{
|
||||
keys = keys ?? Array.Empty<string>();
|
||||
values = values ?? Array.Empty<string>();
|
||||
|
|
@ -38,33 +38,33 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
}
|
||||
|
||||
var zipped = keys.Zip(values, (k, v) => new KeyValuePair<string, object>(k, v));
|
||||
AssertMatch(feature, context, expected, new RouteValueDictionary(zipped));
|
||||
AssertMatch(context, httpContext, expected, new RouteValueDictionary(zipped));
|
||||
}
|
||||
|
||||
public static void AssertMatch(
|
||||
EndpointFeature feature,
|
||||
HttpContext context,
|
||||
EndpointSelectorContext context,
|
||||
HttpContext httpContext,
|
||||
Endpoint expected,
|
||||
RouteValueDictionary values,
|
||||
bool ignoreValues = false)
|
||||
{
|
||||
if (feature.Endpoint == null)
|
||||
if (context.Endpoint == null)
|
||||
{
|
||||
throw new XunitException($"Was expected to match '{expected.DisplayName}' but did not match.");
|
||||
}
|
||||
|
||||
var actualValues = context.Features.Get<IRouteValuesFeature>().RouteValues;
|
||||
var actualValues = httpContext.Features.Get<IRouteValuesFeature>().RouteValues;
|
||||
|
||||
if (actualValues == null)
|
||||
{
|
||||
throw new XunitException("RouteValues is null.");
|
||||
}
|
||||
|
||||
if (!object.ReferenceEquals(expected, feature.Endpoint))
|
||||
if (!object.ReferenceEquals(expected, context.Endpoint))
|
||||
{
|
||||
throw new XunitException(
|
||||
$"Was expected to match '{expected.DisplayName}' but matched " +
|
||||
$"'{feature.Endpoint.DisplayName}' with values: {FormatRouteValues(actualValues)}.");
|
||||
$"'{context.Endpoint.DisplayName}' with values: {FormatRouteValues(actualValues)}.");
|
||||
}
|
||||
|
||||
if (!ignoreValues)
|
||||
|
|
@ -81,13 +81,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
}
|
||||
}
|
||||
|
||||
public static void AssertNotMatch(EndpointFeature feature, HttpContext context)
|
||||
public static void AssertNotMatch(EndpointSelectorContext context, HttpContext httpContext)
|
||||
{
|
||||
if (feature.Endpoint != null)
|
||||
if (context.Endpoint != null)
|
||||
{
|
||||
throw new XunitException(
|
||||
$"Was expected not to match '{feature.Endpoint.DisplayName}' " +
|
||||
$"but matched with values: {FormatRouteValues(context.Features.Get<IRouteValuesFeature>().RouteValues)}.");
|
||||
$"Was expected not to match '{context.Endpoint.DisplayName}' " +
|
||||
$"but matched with values: {FormatRouteValues(httpContext.Features.Get<IRouteValuesFeature>().RouteValues)}.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher("/");
|
||||
var (httpContext, feature) = CreateContext("/");
|
||||
var (httpContext, context) = CreateContext("/");
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -27,13 +27,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher("/simple");
|
||||
var (httpContext, feature) = CreateContext("/simple");
|
||||
var (httpContext, context) = CreateContext("/simple");
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -41,13 +41,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher("/simple");
|
||||
var (httpContext, feature) = CreateContext("/simple/");
|
||||
var (httpContext, context) = CreateContext("/simple/");
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -58,13 +58,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher("/Simple");
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint);
|
||||
}
|
||||
|
||||
// Some matchers will optimize for the ASCII case
|
||||
|
|
@ -75,13 +75,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher(template);
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint);
|
||||
}
|
||||
|
||||
// Matchers should operate on the decoded representation - a matcher that calls
|
||||
|
|
@ -93,13 +93,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher(template);
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -113,13 +113,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher("/simple");
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertNotMatch(feature, httpContext);
|
||||
MatcherAssert.AssertNotMatch(context, httpContext);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -130,13 +130,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher(template);
|
||||
var (httpContext, feature) = CreateContext("/simple");
|
||||
var (httpContext, context) = CreateContext("/simple");
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint);
|
||||
}
|
||||
|
||||
// Matchers do their own 'splitting' of the path into segments, so including
|
||||
|
|
@ -153,13 +153,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher(template);
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint);
|
||||
}
|
||||
|
||||
// Matchers do their own 'splitting' of the path into segments, so including
|
||||
|
|
@ -184,13 +184,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher(template);
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertNotMatch(feature, httpContext);
|
||||
MatcherAssert.AssertNotMatch(context, httpContext);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -198,14 +198,14 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher("/{p}");
|
||||
var (httpContext, feature) = CreateContext("/14");
|
||||
var (httpContext, context) = CreateContext("/14");
|
||||
var values = new RouteValueDictionary(new { p = "14", });
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint, values);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint, values);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -213,14 +213,14 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher("/{p:int}");
|
||||
var (httpContext, feature) = CreateContext("/14");
|
||||
var (httpContext, context) = CreateContext("/14");
|
||||
var values = new RouteValueDictionary(new { p = "14", });
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint, values);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint, values);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -228,14 +228,14 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher("/{p}");
|
||||
var (httpContext, feature) = CreateContext("/14/");
|
||||
var (httpContext, context) = CreateContext("/14/");
|
||||
var values = new RouteValueDictionary(new { p = "14", });
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint, values);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint, values);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -243,7 +243,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher("/foo/{ }/{.!$%}/{dynamic.data}");
|
||||
var (httpContext, feature) = CreateContext("/foo/space/weirdmatch/matcherid");
|
||||
var (httpContext, context) = CreateContext("/foo/space/weirdmatch/matcherid");
|
||||
var values = new RouteValueDictionary()
|
||||
{
|
||||
{ " ", "space" },
|
||||
|
|
@ -252,10 +252,10 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
};
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint, values);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint, values);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -267,13 +267,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher("/{p}");
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertNotMatch(feature, httpContext);
|
||||
MatcherAssert.AssertNotMatch(context, httpContext);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -288,13 +288,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher(template);
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values);
|
||||
MatcherAssert.AssertMatch(context, httpContext, endpoint, keys, values);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -317,13 +317,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
// Arrange
|
||||
var (matcher, endpoint) = CreateMatcher(template);
|
||||
var (httpContext, feature) = CreateContext(path);
|
||||
var (httpContext, context) = CreateContext(path);
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, feature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
MatcherAssert.AssertNotMatch(feature, httpContext);
|
||||
MatcherAssert.AssertNotMatch(context, httpContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,21 +14,21 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
internal abstract Matcher CreateMatcher(params RouteEndpoint[] endpoints);
|
||||
|
||||
internal static (HttpContext httpContext, EndpointFeature feature) CreateContext(string path)
|
||||
internal static (HttpContext httpContext, EndpointSelectorContext context) CreateContext(string path)
|
||||
{
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Request.Method = "TEST";
|
||||
httpContext.Request.Path = path;
|
||||
httpContext.RequestServices = CreateServices();
|
||||
|
||||
var feature = new EndpointFeature
|
||||
var context = new EndpointSelectorContext()
|
||||
{
|
||||
RouteValues = new RouteValueDictionary()
|
||||
};
|
||||
httpContext.Features.Set<IEndpointFeature>(feature);
|
||||
httpContext.Features.Set<IRouteValuesFeature>(feature);
|
||||
httpContext.Features.Set<IEndpointFeature>(context);
|
||||
httpContext.Features.Set<IRouteValuesFeature>(context);
|
||||
|
||||
return (httpContext, feature);
|
||||
return (httpContext, context);
|
||||
}
|
||||
|
||||
// The older routing implementations retrieve services when they first execute.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
|
||||
namespace Microsoft.AspNetCore.Routing.Matching
|
||||
{
|
||||
|
|
@ -18,25 +17,25 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
_inner = inner;
|
||||
}
|
||||
|
||||
public async override Task MatchAsync(HttpContext httpContext, EndpointFeature feature)
|
||||
public async override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context)
|
||||
{
|
||||
if (httpContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(httpContext));
|
||||
}
|
||||
|
||||
if (feature == null)
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(feature));
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var context = new RouteContext(httpContext);
|
||||
await _inner.RouteAsync(context);
|
||||
var routeContext = new RouteContext(httpContext);
|
||||
await _inner.RouteAsync(routeContext);
|
||||
|
||||
if (context.Handler != null)
|
||||
if (routeContext.Handler != null)
|
||||
{
|
||||
feature.RouteValues = context.RouteData.Values;
|
||||
await context.Handler(httpContext);
|
||||
context.RouteValues = routeContext.RouteData.Values;
|
||||
await routeContext.Handler(httpContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
private readonly EndpointSelector _selector;
|
||||
private readonly RouteEndpoint[] _candidates;
|
||||
private readonly RouteValueDictionary[] _values;
|
||||
private readonly int[] _scores;
|
||||
|
||||
public SelectorRouter(EndpointSelector selector, RouteEndpoint[] candidates)
|
||||
|
|
@ -83,6 +84,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
_selector = selector;
|
||||
_candidates = candidates;
|
||||
|
||||
_values = new RouteValueDictionary[_candidates.Length];
|
||||
_scores = new int[_candidates.Length];
|
||||
}
|
||||
|
||||
|
|
@ -91,18 +93,18 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task RouteAsync(RouteContext context)
|
||||
public async Task RouteAsync(RouteContext routeContext)
|
||||
{
|
||||
var feature = (EndpointFeature)context.HttpContext.Features.Get<IEndpointFeature>();
|
||||
var context = (EndpointSelectorContext)routeContext.HttpContext.Features.Get<IEndpointFeature>();
|
||||
|
||||
// This is needed due to a quirk of our tests - they reuse the endpoint feature
|
||||
// across requests.
|
||||
feature.Endpoint = null;
|
||||
context.Endpoint = null;
|
||||
|
||||
await _selector.SelectAsync(context.HttpContext, feature, new CandidateSet(_candidates, _scores));
|
||||
if (feature.Endpoint != null)
|
||||
await _selector.SelectAsync(routeContext.HttpContext, context, new CandidateSet(_candidates, _values, _scores));
|
||||
if (context.Endpoint != null)
|
||||
{
|
||||
context.Handler = (_) => Task.CompletedTask;
|
||||
routeContext.Handler = (_) => Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,25 +19,25 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
_inner = inner;
|
||||
}
|
||||
|
||||
public async override Task MatchAsync(HttpContext httpContext, EndpointFeature feature)
|
||||
public async override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context)
|
||||
{
|
||||
if (httpContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(httpContext));
|
||||
}
|
||||
|
||||
if (feature == null)
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(feature));
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var context = new RouteContext(httpContext);
|
||||
await _inner.RouteAsync(context);
|
||||
var routeContext = new RouteContext(httpContext);
|
||||
await _inner.RouteAsync(routeContext);
|
||||
|
||||
if (context.Handler != null)
|
||||
if (routeContext.Handler != null)
|
||||
{
|
||||
feature.RouteValues = context.RouteData.Values;
|
||||
await context.Handler(httpContext);
|
||||
context.RouteValues = routeContext.RouteData.Values;
|
||||
await routeContext.Handler(httpContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
private readonly EndpointSelector _selector;
|
||||
private readonly RouteEndpoint[] _candidates;
|
||||
private readonly RouteValueDictionary[] _values;
|
||||
private readonly int[] _scores;
|
||||
|
||||
public SelectorRouter(EndpointSelector selector, RouteEndpoint[] candidates)
|
||||
|
|
@ -85,6 +86,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
_selector = selector;
|
||||
_candidates = candidates;
|
||||
|
||||
_values = new RouteValueDictionary[_candidates.Length];
|
||||
_scores = new int[_candidates.Length];
|
||||
}
|
||||
|
||||
|
|
@ -93,17 +95,17 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task RouteAsync(RouteContext context)
|
||||
public async Task RouteAsync(RouteContext routeContext)
|
||||
{
|
||||
var feature = (EndpointFeature)context.HttpContext.Features.Get<IEndpointFeature>();
|
||||
var context = (EndpointSelectorContext)routeContext.HttpContext.Features.Get<IEndpointFeature>();
|
||||
|
||||
// This is needed due to a quirk of our tests - they reuse the endpoint feature.
|
||||
feature.Endpoint = null;
|
||||
context.Endpoint = null;
|
||||
|
||||
await _selector.SelectAsync(context.HttpContext, feature, new CandidateSet(_candidates, _scores));
|
||||
if (feature.Endpoint != null)
|
||||
await _selector.SelectAsync(routeContext.HttpContext, context, new CandidateSet(_candidates, _values, _scores));
|
||||
if (context.Endpoint != null)
|
||||
{
|
||||
context.Handler = (_) => Task.CompletedTask;
|
||||
routeContext.Handler = (_) => Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,12 +17,12 @@ namespace Microsoft.AspNetCore.Routing.TestObjects
|
|||
_isHandled = isHandled;
|
||||
}
|
||||
|
||||
public override Task MatchAsync(HttpContext httpContext, EndpointFeature feature)
|
||||
public override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context)
|
||||
{
|
||||
if (_isHandled)
|
||||
{
|
||||
feature.RouteValues = new RouteValueDictionary(new { controller = "Home", action = "Index" });
|
||||
feature.Endpoint = new Endpoint(TestConstants.EmptyRequestDelegate, EndpointMetadataCollection.Empty, "Test endpoint");
|
||||
context.RouteValues = new RouteValueDictionary(new { controller = "Home", action = "Index" });
|
||||
context.Endpoint = new Endpoint(TestConstants.EmptyRequestDelegate, EndpointMetadataCollection.Empty, "Test endpoint");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
|
|
|||
Loading…
Reference in New Issue