React to IEndpointSelectorPolicy changes
This commit is contained in:
parent
5bddd226a3
commit
384b814349
|
|
@ -47,8 +47,8 @@
|
||||||
<MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>
|
<MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>
|
||||||
<MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>
|
<MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>
|
||||||
<MicrosoftAspNetCoreResponseCachingPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreResponseCachingPackageVersion>
|
<MicrosoftAspNetCoreResponseCachingPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreResponseCachingPackageVersion>
|
||||||
<MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>2.2.0-a-preview3-endpoint-selector-17041</MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>
|
<MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>2.2.0-a-preview3-matcher-policy-17051</MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>
|
||||||
<MicrosoftAspNetCoreRoutingPackageVersion>2.2.0-a-preview3-endpoint-selector-17041</MicrosoftAspNetCoreRoutingPackageVersion>
|
<MicrosoftAspNetCoreRoutingPackageVersion>2.2.0-a-preview3-matcher-policy-17051</MicrosoftAspNetCoreRoutingPackageVersion>
|
||||||
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
|
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
|
||||||
<MicrosoftAspNetCoreServerKestrelPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreServerKestrelPackageVersion>
|
<MicrosoftAspNetCoreServerKestrelPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreServerKestrelPackageVersion>
|
||||||
<MicrosoftAspNetCoreSessionPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreSessionPackageVersion>
|
<MicrosoftAspNetCoreSessionPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreSessionPackageVersion>
|
||||||
|
|
|
||||||
|
|
@ -150,10 +150,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var actionConstraints = new IActionConstraint[count];
|
var actionConstraints = new IActionConstraint[count];
|
||||||
var actionConstraintIndex = 0;
|
var actionConstraintIndex = 0;
|
||||||
for (int i = 0; i < items.Count; i++)
|
for (var i = 0; i < items.Count; i++)
|
||||||
{
|
{
|
||||||
var actionConstraint = items[i].Constraint;
|
var actionConstraint = items[i].Constraint;
|
||||||
if (actionConstraint != null)
|
if (actionConstraint != null)
|
||||||
|
|
@ -168,7 +168,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
internal class InnerCache
|
internal class InnerCache
|
||||||
{
|
{
|
||||||
private readonly ActionDescriptorCollection _actions;
|
private readonly ActionDescriptorCollection _actions;
|
||||||
private bool? _hasActionConstraints;
|
|
||||||
|
|
||||||
public InnerCache(ActionDescriptorCollection actions)
|
public InnerCache(ActionDescriptorCollection actions)
|
||||||
{
|
{
|
||||||
|
|
@ -179,61 +178,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
new ConcurrentDictionary<ActionDescriptor, CacheEntry>();
|
new ConcurrentDictionary<ActionDescriptor, CacheEntry>();
|
||||||
|
|
||||||
public int Version => _actions.Version;
|
public int Version => _actions.Version;
|
||||||
|
|
||||||
public bool HasActionConstraints
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
// This is a safe race-condition, since it always transitions from null to non-null.
|
|
||||||
// All writers will always get the same result.
|
|
||||||
if (_hasActionConstraints == null)
|
|
||||||
{
|
|
||||||
var found = false;
|
|
||||||
for (var i = 0; i < _actions.Items.Count; i++)
|
|
||||||
{
|
|
||||||
var action = _actions.Items[i];
|
|
||||||
if (action.ActionConstraints?.Count > 0 && HasSignificantActionConstraint(action))
|
|
||||||
{
|
|
||||||
// We need to check for some specific action constraint implementations.
|
|
||||||
// We've implemented consumes, and HTTP method support inside endpoint routing, so
|
|
||||||
// we don't need to run an 'action constraint phase' if those are the only constraints.
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_hasActionConstraints = found;
|
|
||||||
|
|
||||||
bool HasSignificantActionConstraint(ActionDescriptor action)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < action.ActionConstraints.Count; i++)
|
|
||||||
{
|
|
||||||
var actionConstraint = action.ActionConstraints[i];
|
|
||||||
if (actionConstraint.GetType() == typeof(HttpMethodActionConstraint))
|
|
||||||
{
|
|
||||||
// This one is OK, we implement this in endpoint routing.
|
|
||||||
}
|
|
||||||
else if (actionConstraint.GetType().FullName == "Microsoft.AspNetCore.Mvc.Cors.Internal.CorsHttpMethodActionConstraint")
|
|
||||||
{
|
|
||||||
// This one is OK, we implement this in endpoint routing.
|
|
||||||
}
|
|
||||||
else if (actionConstraint.GetType() == typeof(ConsumesAttribute))
|
|
||||||
{
|
|
||||||
// This one is OK, we implement this in endpoint routing.
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _hasActionConstraints.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal readonly struct CacheEntry
|
internal readonly struct CacheEntry
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ using Microsoft.AspNetCore.Mvc.ActionConstraints;
|
||||||
using Microsoft.AspNetCore.Mvc.Internal;
|
using Microsoft.AspNetCore.Mvc.Internal;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.AspNetCore.Routing.Matching;
|
using Microsoft.AspNetCore.Routing.Matching;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.Routing
|
namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
{
|
{
|
||||||
|
|
@ -24,7 +23,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
// We need to be able to run IActionConstraints on Endpoints that aren't associated
|
// We need to be able to run IActionConstraints on Endpoints that aren't associated
|
||||||
// with an action. This is a sentinel value we use when the endpoint isn't from MVC.
|
// with an action. This is a sentinel value we use when the endpoint isn't from MVC.
|
||||||
internal static readonly ActionDescriptor NonAction = new ActionDescriptor();
|
internal static readonly ActionDescriptor NonAction = new ActionDescriptor();
|
||||||
|
|
||||||
private readonly ActionConstraintCache _actionConstraintCache;
|
private readonly ActionConstraintCache _actionConstraintCache;
|
||||||
|
|
||||||
public ActionConstraintMatcherPolicy(ActionConstraintCache actionConstraintCache)
|
public ActionConstraintMatcherPolicy(ActionConstraintCache actionConstraintCache)
|
||||||
|
|
@ -35,27 +34,59 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
// Run really late.
|
// Run really late.
|
||||||
public override int Order => 100000;
|
public override int Order => 100000;
|
||||||
|
|
||||||
// Internal for testing
|
public bool AppliesToEndpoints(IReadOnlyList<Endpoint> endpoints)
|
||||||
internal bool ShouldRunActionConstraints => _actionConstraintCache.CurrentCache.HasActionConstraints;
|
|
||||||
|
|
||||||
public Task ApplyAsync(HttpContext httpContext, EndpointSelectorContext context, CandidateSet candidateSet)
|
|
||||||
{
|
{
|
||||||
// PERF: we can skip over action constraints if there aren't any app-wide.
|
if (endpoints == null)
|
||||||
//
|
|
||||||
// Running action constraints (or just checking for them) in a candidate set
|
|
||||||
// is somewhat expensive compared to other routing operations. This should only
|
|
||||||
// happen if user-code adds action constraints.
|
|
||||||
if (ShouldRunActionConstraints)
|
|
||||||
{
|
{
|
||||||
ApplyActionConstraints(httpContext, candidateSet);
|
throw new ArgumentNullException(nameof(endpoints));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
// We can skip over action constraints when they aren't any for this set
|
||||||
|
// of endpoints. This happens once on startup so it removes this component
|
||||||
|
// from the code path in most scenarios.
|
||||||
|
for (var i = 0; i < endpoints.Count; i++)
|
||||||
|
{
|
||||||
|
var endpoint = endpoints[i];
|
||||||
|
var action = endpoint.Metadata.GetMetadata<ActionDescriptor>();
|
||||||
|
if (action?.ActionConstraints?.Count > 0 && HasSignificantActionConstraint(action))
|
||||||
|
{
|
||||||
|
// We need to check for some specific action constraint implementations.
|
||||||
|
// We've implemented consumes, and HTTP method support inside endpoint routing, so
|
||||||
|
// we don't need to run an 'action constraint phase' if those are the only constraints.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool HasSignificantActionConstraint(ActionDescriptor a)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < a.ActionConstraints.Count; i++)
|
||||||
|
{
|
||||||
|
var actionConstraint = a.ActionConstraints[i];
|
||||||
|
if (actionConstraint.GetType() == typeof(HttpMethodActionConstraint))
|
||||||
|
{
|
||||||
|
// This one is OK, we implement this in endpoint routing.
|
||||||
|
}
|
||||||
|
else if (actionConstraint.GetType().FullName == "Microsoft.AspNetCore.Mvc.Cors.Internal.CorsHttpMethodActionConstraint")
|
||||||
|
{
|
||||||
|
// This one is OK, we implement this in endpoint routing.
|
||||||
|
}
|
||||||
|
else if (actionConstraint.GetType() == typeof(ConsumesAttribute))
|
||||||
|
{
|
||||||
|
// This one is OK, we implement this in endpoint routing.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyActionConstraints(
|
public Task ApplyAsync(HttpContext httpContext, EndpointSelectorContext context, CandidateSet candidateSet)
|
||||||
HttpContext httpContext,
|
|
||||||
CandidateSet candidateSet)
|
|
||||||
{
|
{
|
||||||
var finalMatches = EvaluateActionConstraints(httpContext, candidateSet);
|
var finalMatches = EvaluateActionConstraints(httpContext, candidateSet);
|
||||||
|
|
||||||
|
|
@ -74,6 +105,8 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
candidateSet.SetValidity(finalMatches[i].index, true);
|
candidateSet.SetValidity(finalMatches[i].index, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is almost the same as the code in ActionSelector, but we can't really share the logic
|
// This is almost the same as the code in ActionSelector, but we can't really share the logic
|
||||||
|
|
@ -171,8 +204,10 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
var endpointsWithConstraint = new List<(int index, ActionSelectorCandidate candidate)>();
|
var endpointsWithConstraint = new List<(int index, ActionSelectorCandidate candidate)>();
|
||||||
var endpointsWithoutConstraint = new List<(int index, ActionSelectorCandidate candidate)>();
|
var endpointsWithoutConstraint = new List<(int index, ActionSelectorCandidate candidate)>();
|
||||||
|
|
||||||
var constraintContext = new ActionConstraintContext();
|
var constraintContext = new ActionConstraintContext
|
||||||
constraintContext.Candidates = items.Select(i => i.candidate).ToArray();
|
{
|
||||||
|
Candidates = items.Select(i => i.candidate).ToArray()
|
||||||
|
};
|
||||||
|
|
||||||
// Perf: Avoid allocations
|
// Perf: Avoid allocations
|
||||||
for (var i = 0; i < items.Count; i++)
|
for (var i = 0; i < items.Count; i++)
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
|
|
||||||
public IComparer<Endpoint> Comparer { get; } = new ConsumesMetadataEndpointComparer();
|
public IComparer<Endpoint> Comparer { get; } = new ConsumesMetadataEndpointComparer();
|
||||||
|
|
||||||
public bool AppliesToNode(IReadOnlyList<Endpoint> endpoints)
|
public bool AppliesToEndpoints(IReadOnlyList<Endpoint> endpoints)
|
||||||
{
|
{
|
||||||
if (endpoints == null)
|
if (endpoints == null)
|
||||||
{
|
{
|
||||||
|
|
@ -173,7 +173,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
return new ConsumesPolicyJumpTable(exitDestination, ordered);
|
return new ConsumesPolicyJumpTable(exitDestination, ordered);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetScore(MediaType mediaType)
|
private int GetScore(in MediaType mediaType)
|
||||||
{
|
{
|
||||||
// Higher score == lower priority - see comments on MediaType.
|
// Higher score == lower priority - see comments on MediaType.
|
||||||
if (mediaType.MatchesAllTypes)
|
if (mediaType.MatchesAllTypes)
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
public class ConsumesMatcherPolicyTest
|
public class ConsumesMatcherPolicyTest
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AppliesToNode_EndpointWithoutMetadata_ReturnsFalse()
|
public void AppliesToEndpoints_EndpointWithoutMetadata_ReturnsFalse()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var endpoints = new[] { CreateEndpoint("/", null), };
|
var endpoints = new[] { CreateEndpoint("/", null), };
|
||||||
|
|
@ -24,14 +24,14 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
var policy = CreatePolicy();
|
var policy = CreatePolicy();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = policy.AppliesToNode(endpoints);
|
var result = policy.AppliesToEndpoints(endpoints);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result);
|
Assert.False(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AppliesToNode_EndpointWithoutContentTypes_ReturnsFalse()
|
public void AppliesToEndpoints_EndpointWithoutContentTypes_ReturnsFalse()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var endpoints = new[]
|
var endpoints = new[]
|
||||||
|
|
@ -42,14 +42,14 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
var policy = CreatePolicy();
|
var policy = CreatePolicy();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = policy.AppliesToNode(endpoints);
|
var result = policy.AppliesToEndpoints(endpoints);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result);
|
Assert.False(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AppliesToNode_EndpointHasContentTypes_ReturnsTrue()
|
public void AppliesToEndpoints_EndpointHasContentTypes_ReturnsTrue()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var endpoints = new[]
|
var endpoints = new[]
|
||||||
|
|
@ -61,7 +61,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
var policy = CreatePolicy();
|
var policy = CreatePolicy();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = policy.AppliesToNode(endpoints);
|
var result = policy.AppliesToEndpoints(endpoints);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(result);
|
Assert.True(result);
|
||||||
|
|
|
||||||
|
|
@ -338,7 +338,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ShouldRunActionConstraints_IgnoresIgnorableConstraints()
|
public void AppliesToEndpoints_IgnoresIgnorableConstraints()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var actions = new ActionDescriptor[]
|
var actions = new ActionDescriptor[]
|
||||||
|
|
@ -369,11 +369,12 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
var endpoints = actions.Select(CreateEndpoint).ToArray();
|
||||||
|
|
||||||
var selector = CreateSelector(actions);
|
var selector = CreateSelector(actions);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = selector.ShouldRunActionConstraints;
|
var result = selector.AppliesToEndpoints(endpoints);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result);
|
Assert.False(result);
|
||||||
|
|
@ -397,11 +398,12 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
var endpoints = actions.Select(CreateEndpoint).ToArray();
|
||||||
|
|
||||||
var selector = CreateSelector(actions);
|
var selector = CreateSelector(actions);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = selector.ShouldRunActionConstraints;
|
var result = selector.AppliesToEndpoints(endpoints);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(result);
|
Assert.True(result);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue