Merge pull request #746 from dotnet-maestro-bot/merge/release/2.2-to-master

[automated] Merge branch 'release/2.2' => 'master'
This commit is contained in:
Ryan Nowak 2018-08-22 20:38:02 -07:00 committed by GitHub
commit 61c5f2bad4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 73 additions and 7 deletions

View File

@ -24,16 +24,30 @@ namespace Microsoft.AspNetCore.Routing.Matching
_selectorPolicies = matcherPolicies.OrderBy(p => p.Order).OfType<IEndpointSelectorPolicy>().ToArray();
}
public override Task SelectAsync(
public override async Task SelectAsync(
HttpContext httpContext,
EndpointFeature feature,
CandidateSet candidateSet)
{
var selectorPolicies = _selectorPolicies;
for (var i = 0; i < _selectorPolicies.Length; i++)
{
_selectorPolicies[i].Apply(httpContext, candidateSet);
await selectorPolicies[i].ApplyAsync(httpContext, feature, candidateSet);
if (feature.Endpoint != null)
{
// This is a short circuit, the selector chose an endpoint.
return;
}
}
ProcessFinalCandidates(httpContext, feature, candidateSet);
}
private static void ProcessFinalCandidates(
HttpContext httpContext,
EndpointFeature feature,
CandidateSet candidateSet)
{
RouteEndpoint endpoint = null;
RouteValueDictionary values = null;
int? foundScore = null;
@ -76,8 +90,6 @@ namespace Microsoft.AspNetCore.Routing.Matching
feature.Endpoint = endpoint;
feature.RouteValues = values;
}
return Task.CompletedTask;
}
private static void ReportAmbiguity(CandidateSet candidates)

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Routing.Matching
{
@ -19,12 +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>
/// <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.
/// </para>
/// <para>
/// To signal an error condition, set <see cref="EndpointFeature.Endpoint"/> to an
/// <see cref="Endpoint"/> value that will produce the desired error when executed.
/// </para>
/// </remarks>
void Apply(HttpContext httpContext, CandidateSet candidates);
Task ApplyAsync(HttpContext httpContext, EndpointFeature feature, CandidateSet candidates);
}
}

View File

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
@ -184,10 +185,11 @@ test: /test3", ex.Message);
var policy = new Mock<MatcherPolicy>();
policy
.As<IEndpointSelectorPolicy>()
.Setup(p => p.Apply(It.IsAny<HttpContext>(), It.IsAny<CandidateSet>()))
.Callback<HttpContext, CandidateSet>((c, cs) =>
.Setup(p => p.ApplyAsync(It.IsAny<HttpContext>(), It.IsAny<EndpointFeature>(), It.IsAny<CandidateSet>()))
.Returns<HttpContext, EndpointFeature, CandidateSet>((c, f, cs) =>
{
cs[1].IsValidCandidate = false;
return Task.CompletedTask;
});
candidateSet[0].IsValidCandidate = false;
@ -204,6 +206,48 @@ test: /test3", ex.Message);
Assert.Same(endpoints[2], feature.Endpoint);
}
[Fact]
public async Task SelectAsync_RunsEndpointSelectorPolicies_CanShortCircuit()
{
// Arrange
var endpoints = new RouteEndpoint[] { CreateEndpoint("/test1"), CreateEndpoint("/test2"), CreateEndpoint("/test3"), };
var scores = new int[] { 0, 0, 1 };
var candidateSet = CreateCandidateSet(endpoints, scores);
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) =>
{
f.Endpoint = cs[0].Endpoint;
return Task.CompletedTask;
});
// This should never run, it's after policy1 which short circuits
var policy2 = new Mock<MatcherPolicy>();
policy2
.SetupGet(p => p.Order)
.Returns(1000);
policy2
.As<IEndpointSelectorPolicy>()
.Setup(p => p.ApplyAsync(It.IsAny<HttpContext>(), It.IsAny<EndpointFeature>(), It.IsAny<CandidateSet>()))
.Throws(new InvalidOperationException());
candidateSet[0].IsValidCandidate = false;
candidateSet[1].IsValidCandidate = true;
candidateSet[2].IsValidCandidate = true;
var (httpContext, feature) = CreateContext();
var selector = CreateSelector(policy1.Object, policy2.Object);
// Act
await selector.SelectAsync(httpContext, feature, candidateSet);
// Assert
Assert.Same(endpoints[0], feature.Endpoint);
}
private static (HttpContext httpContext, EndpointFeature feature) CreateContext()
{
var feature = new EndpointFeature();