Merge branch 'merge/release/2.2-to-master'

This commit is contained in:
James Newton-King 2018-08-22 15:58:38 +12:00
commit a15f213e43
No known key found for this signature in database
GPG Key ID: 0A66B2F456BF5526
40 changed files with 769 additions and 994 deletions

View File

@ -136,7 +136,7 @@ namespace Microsoft.AspNetCore.Routing
new RouteTemplate(RoutePatternFactory.Parse(
endpoint.RoutePattern.RawText,
defaults: endpoint.RoutePattern.Defaults,
constraints: null)),
parameterPolicies: null)),
requiredLinkValues: new RouteValueDictionary(requiredValues),
routeName: null,
order: 0);

View File

@ -1,61 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Globalization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Matching;
using Microsoft.Extensions.Logging;
namespace RoutingSample.Web
{
internal class EndsWithStringMatchProcessor : MatchProcessor
{
private readonly ILogger<EndsWithStringMatchProcessor> _logger;
public EndsWithStringMatchProcessor(ILogger<EndsWithStringMatchProcessor> logger)
{
_logger = logger;
}
public string ParameterName { get; private set; }
public string ConstraintArgument { get; private set; }
public override void Initialize(string parameterName, string constraintArgument)
{
ParameterName = parameterName;
ConstraintArgument = constraintArgument;
}
public override bool ProcessInbound(HttpContext httpContext, RouteValueDictionary values)
{
return Process(values);
}
public override bool ProcessOutbound(HttpContext httpContext, RouteValueDictionary values)
{
return Process(values);
}
private bool Process(RouteValueDictionary values)
{
if (!values.TryGetValue(ParameterName, out var value) || value == null)
{
return false;
}
var valueString = Convert.ToString(value, CultureInfo.InvariantCulture);
var endsWith = valueString.EndsWith(ConstraintArgument, StringComparison.OrdinalIgnoreCase);
if (!endsWith)
{
_logger.LogDebug(
$"Parameter '{ParameterName}' with value '{valueString}' does not end with '{ConstraintArgument}'.");
}
return endsWith;
}
}
}

View File

@ -0,0 +1,33 @@
// 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.Globalization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
namespace RoutingSample.Web
{
internal class EndsWithStringRouteConstraint : IRouteConstraint
{
private readonly string _endsWith;
public EndsWithStringRouteConstraint(string endsWith)
{
_endsWith = endsWith;
}
public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
{
var value = values[routeKey];
if (value == null)
{
return false;
}
var valueString = Convert.ToString(value, CultureInfo.InvariantCulture);
var endsWith = valueString.EndsWith(_endsWith, StringComparison.OrdinalIgnoreCase);
return endsWith;
}
}
}

View File

@ -21,11 +21,11 @@ namespace RoutingSample.Web
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<EndsWithStringMatchProcessor>();
services.AddTransient<EndsWithStringRouteConstraint>();
services.AddRouting(options =>
{
options.ConstraintMap.Add("endsWith", typeof(EndsWithStringMatchProcessor));
options.ConstraintMap.Add("endsWith", typeof(EndsWithStringRouteConstraint));
});
}

View File

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Routing
{
/// <summary>
/// A marker interface for types that are associated with route parameters.
/// </summary>
public interface IParameterPolicy
{
}
}

View File

@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Routing
/// Defines the contract that a class must implement in order to check whether a URL parameter
/// value is valid for a constraint.
/// </summary>
public interface IRouteConstraint
public interface IRouteConstraint : IParameterPolicy
{
/// <summary>
/// Determines whether the URL parameter contains a valid value for this constraint.

View File

@ -19,20 +19,20 @@ namespace Microsoft.AspNetCore.Routing
internal class DefaultLinkGenerator : LinkGenerator
{
private readonly static char[] UrlQueryDelimiters = new char[] { '?', '#' };
private readonly MatchProcessorFactory _matchProcessorFactory;
private readonly ParameterPolicyFactory _parameterPolicyFactory;
private readonly ObjectPool<UriBuildingContext> _uriBuildingContextPool;
private readonly ILogger<DefaultLinkGenerator> _logger;
private readonly IServiceProvider _serviceProvider;
private readonly RouteOptions _options;
public DefaultLinkGenerator(
MatchProcessorFactory matchProcessorFactory,
ParameterPolicyFactory parameterPolicyFactory,
ObjectPool<UriBuildingContext> uriBuildingContextPool,
IOptions<RouteOptions> routeOptions,
ILogger<DefaultLinkGenerator> logger,
IServiceProvider serviceProvider)
{
_matchProcessorFactory = matchProcessorFactory;
_parameterPolicyFactory = parameterPolicyFactory;
_uriBuildingContextPool = uriBuildingContextPool;
_options = routeOptions.Value;
_logger = logger;
@ -240,15 +240,16 @@ namespace Microsoft.AspNetCore.Routing
throw new ArgumentNullException(nameof(routeValues));
}
foreach (var kvp in endpoint.RoutePattern.Constraints)
foreach (var kvp in endpoint.RoutePattern.ParameterPolicies)
{
var parameter = endpoint.RoutePattern.GetParameter(kvp.Key); // may be null, that's ok
var constraintReferences = kvp.Value;
for (var i = 0; i < constraintReferences.Count; i++)
{
var constraintReference = constraintReferences[i];
var matchProcessor = _matchProcessorFactory.Create(parameter, constraintReference);
if (!matchProcessor.ProcessOutbound(httpContext, routeValues))
var parameterPolicy = _parameterPolicyFactory.Create(parameter, constraintReference);
if (parameterPolicy is IRouteConstraint routeConstraint
&& !routeConstraint.Match(httpContext, NullRouter.Instance, kvp.Key, routeValues, RouteDirection.UrlGeneration))
{
return false;
}

View File

@ -2,17 +2,19 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Routing.Constraints;
using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Routing.Matching
namespace Microsoft.AspNetCore.Routing
{
internal class DefaultMatchProcessorFactory : MatchProcessorFactory
internal class DefaultParameterPolicyFactory : ParameterPolicyFactory
{
private readonly RouteOptions _options;
private readonly IServiceProvider _serviceProvider;
public DefaultMatchProcessorFactory(
public DefaultParameterPolicyFactory(
IOptions<RouteOptions> options,
IServiceProvider serviceProvider)
{
@ -20,31 +22,26 @@ namespace Microsoft.AspNetCore.Routing.Matching
_serviceProvider = serviceProvider;
}
public override MatchProcessor Create(string parameterName, IRouteConstraint value, bool optional)
public override IParameterPolicy Create(RoutePatternParameterPart parameter, IParameterPolicy parameterPolicy)
{
if (value == null)
if (parameterPolicy == null)
{
throw new ArgumentNullException(nameof(value));
throw new ArgumentNullException(nameof(parameterPolicy));
}
return InitializeMatchProcessor(parameterName, optional, value, argument: null);
}
public override MatchProcessor Create(string parameterName, MatchProcessor value, bool optional)
{
if (value == null)
if (parameterPolicy is IRouteConstraint routeConstraint)
{
throw new ArgumentNullException(nameof(value));
return InitializeRouteConstraint(parameter?.IsOptional ?? false, routeConstraint, argument: null);
}
return InitializeMatchProcessor(parameterName, optional, value, argument: null);
return parameterPolicy;
}
public override MatchProcessor Create(string parameterName, string value, bool optional)
public override IParameterPolicy Create(RoutePatternParameterPart parameter, string inlineText)
{
if (value == null)
if (inlineText == null)
{
throw new ArgumentNullException(nameof(value));
throw new ArgumentNullException(nameof(inlineText));
}
// Example:
@ -54,7 +51,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
// value: regex(\d+)
// name: regex
// argument: \d+
(var name, var argument) = Parse(value);
(var name, var argument) = Parse(inlineText);
if (!_options.ConstraintMap.TryGetValue(name, out var type))
{
@ -64,49 +61,37 @@ namespace Microsoft.AspNetCore.Routing.Matching
nameof(RouteOptions.ConstraintMap)));
}
if (typeof(MatchProcessor).IsAssignableFrom(type))
{
var matchProcessor = (MatchProcessor)_serviceProvider.GetRequiredService(type);
return InitializeMatchProcessor(parameterName, optional, matchProcessor, argument);
}
if (typeof(IRouteConstraint).IsAssignableFrom(type))
{
var constraint = DefaultInlineConstraintResolver.CreateConstraint(type, argument);
return InitializeMatchProcessor(parameterName, optional, constraint, argument);
return InitializeRouteConstraint(parameter?.IsOptional ?? false, constraint, argument);
}
if (typeof(IParameterPolicy).IsAssignableFrom(type))
{
var parameterPolicy = (IParameterPolicy)_serviceProvider.GetRequiredService(type);
return parameterPolicy;
}
var message = Resources.FormatRoutePattern_InvalidStringConstraintReference(
type,
name,
typeof(IRouteConstraint),
typeof(MatchProcessor));
typeof(IParameterPolicy));
throw new InvalidOperationException(message);
}
private MatchProcessor InitializeMatchProcessor(
string parameterName,
private IParameterPolicy InitializeRouteConstraint(
bool optional,
IRouteConstraint constraint,
string argument)
{
var matchProcessor = (MatchProcessor)new RouteConstraintMatchProcessor(parameterName, constraint);
return InitializeMatchProcessor(parameterName, optional, matchProcessor, argument);
}
private MatchProcessor InitializeMatchProcessor(
string parameterName,
bool optional,
MatchProcessor matchProcessor,
IRouteConstraint routeConstraint,
string argument)
{
if (optional)
{
matchProcessor = new OptionalMatchProcessor(matchProcessor);
routeConstraint = new OptionalRouteConstraint(routeConstraint);
}
matchProcessor.Initialize(parameterName, argument);
return matchProcessor;
return routeConstraint;
}
private (string name, string argument) Parse(string text)

View File

@ -68,7 +68,7 @@ namespace Microsoft.Extensions.DependencyInjection
//
// Default matcher implementation
//
services.TryAddSingleton<MatchProcessorFactory, DefaultMatchProcessorFactory>();
services.TryAddSingleton<ParameterPolicyFactory, DefaultParameterPolicyFactory>();
services.TryAddSingleton<MatcherFactory, DfaMatcherFactory>();
services.TryAddTransient<DfaMatcherBuilder>();
services.TryAddSingleton<DfaGraphWriter>();

View File

@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
// RouteValueDictionary.
public readonly (RoutePatternPathSegment pathSegment, int segmentIndex)[] ComplexSegments;
public readonly MatchProcessor[] MatchProcessors;
public readonly KeyValuePair<string, IRouteConstraint>[] Constraints;
// Score is a sequential integer value that in determines the priority of an Endpoint.
// Scores are computed within the context of candidate set, and are meaningless when
@ -56,7 +56,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
Captures = Array.Empty<(string parameterName, int segmentIndex, int slotIndex)>();
CatchAll = default;
ComplexSegments = Array.Empty<(RoutePatternPathSegment pathSegment, int segmentIndex)>();
MatchProcessors = Array.Empty<MatchProcessor>();
Constraints = Array.Empty<KeyValuePair<string, IRouteConstraint>>();
Score = 0;
Flags = CandidateFlags.None;
@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
(string parameterName, int segmentIndex, int slotIndex)[] captures,
(string parameterName, int segmentIndex, int slotIndex) catchAll,
(RoutePatternPathSegment pathSegment, int segmentIndex)[] complexSegments,
MatchProcessor[] matchProcessors)
KeyValuePair<string, IRouteConstraint>[] constraints)
{
Endpoint = endpoint;
Score = score;
@ -77,7 +77,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
Captures = captures;
CatchAll = catchAll;
ComplexSegments = complexSegments;
MatchProcessors = matchProcessors;
Constraints = constraints;
Flags = CandidateFlags.None;
for (var i = 0; i < slots.Length; i++)
@ -103,9 +103,9 @@ namespace Microsoft.AspNetCore.Routing.Matching
Flags |= CandidateFlags.HasComplexSegments;
}
if (matchProcessors.Length > 0)
if (constraints.Length > 0)
{
Flags |= CandidateFlags.HasMatchProcessors;
Flags |= CandidateFlags.HasConstraints;
}
}
@ -118,7 +118,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
HasCatchAll = 4,
HasSlots = HasDefaults | HasCaptures | HasCatchAll,
HasComplexSegments = 8,
HasMatchProcessors = 16,
HasConstraints = 16,
}
}
}

View File

@ -123,9 +123,9 @@ namespace Microsoft.AspNetCore.Routing.Matching
isMatch &= ProcessComplexSegments(candidate.ComplexSegments, path, segments, values);
}
if ((flags & Candidate.CandidateFlags.HasMatchProcessors) != 0)
if ((flags & Candidate.CandidateFlags.HasConstraints) != 0)
{
isMatch &= ProcessMatchProcessors(candidate.MatchProcessors, httpContext, values);
isMatch &= ProcessConstraints(candidate.Constraints, httpContext, values);
}
state.IsValidCandidate = isMatch;
@ -215,15 +215,15 @@ namespace Microsoft.AspNetCore.Routing.Matching
return true;
}
private bool ProcessMatchProcessors(
MatchProcessor[] matchProcessors,
private bool ProcessConstraints(
KeyValuePair<string, IRouteConstraint>[] constraints,
HttpContext httpContext,
RouteValueDictionary values)
{
for (var i = 0; i < matchProcessors.Length; i++)
for (var i = 0; i < constraints.Length; i++)
{
var matchProcessor = matchProcessors[i];
if (!matchProcessor.ProcessInbound(httpContext, values))
var constraint = constraints[i];
if (!constraint.Value.Match(httpContext, NullRouter.Instance, constraint.Key, values, RouteDirection.IncomingRequest))
{
return false;
}

View File

@ -12,18 +12,18 @@ namespace Microsoft.AspNetCore.Routing.Matching
{
private readonly List<MatcherEndpoint> _endpoints = new List<MatcherEndpoint>();
private readonly MatchProcessorFactory _matchProcessorFactory;
private readonly ParameterPolicyFactory _parameterPolicyFactory;
private readonly EndpointSelector _selector;
private readonly MatcherPolicy[] _policies;
private readonly INodeBuilderPolicy[] _nodeBuilders;
private readonly MatcherEndpointComparer _comparer;
public DfaMatcherBuilder(
MatchProcessorFactory matchProcessorFactory,
ParameterPolicyFactory parameterPolicyFactory,
EndpointSelector selector,
IEnumerable<MatcherPolicy> policies)
{
_matchProcessorFactory = matchProcessorFactory;
_parameterPolicyFactory = parameterPolicyFactory;
_selector = selector;
_policies = policies.OrderBy(p => p.Order).ToArray();
@ -425,16 +425,19 @@ namespace Microsoft.AspNetCore.Routing.Matching
complexSegments.Add((segment, i));
}
var matchProcessors = new List<MatchProcessor>();
foreach (var kvp in endpoint.RoutePattern.Constraints)
var constraints = new List<KeyValuePair<string, IRouteConstraint>>();
foreach (var kvp in endpoint.RoutePattern.ParameterPolicies)
{
var parameter = endpoint.RoutePattern.GetParameter(kvp.Key); // may be null, that's ok
var constraintReferences = kvp.Value;
for (var i = 0; i < constraintReferences.Count; i++)
var parameterPolicyReferences = kvp.Value;
for (var i = 0; i < parameterPolicyReferences.Count; i++)
{
var constraintReference = constraintReferences[i];
var matchProcessor = _matchProcessorFactory.Create(parameter, constraintReference);
matchProcessors.Add(matchProcessor);
var reference = parameterPolicyReferences[i];
var parameterPolicy = _parameterPolicyFactory.Create(parameter, reference);
if (parameterPolicy is IRouteConstraint routeConstraint)
{
constraints.Add(new KeyValuePair<string, IRouteConstraint>(kvp.Key, routeConstraint));
}
}
}
@ -445,7 +448,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
captures.ToArray(),
catchAll,
complexSegments.ToArray(),
matchProcessors.ToArray());
constraints.ToArray());
}
private int[] GetGroupLengths(DfaNode node)

View File

@ -1,18 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing.Matching
{
public abstract class MatchProcessor
{
public virtual void Initialize(string parameterName, string constraintArgument)
{
}
public abstract bool ProcessInbound(HttpContext httpContext, RouteValueDictionary values);
public abstract bool ProcessOutbound(HttpContext httpContext, RouteValueDictionary values);
}
}

View File

@ -1,46 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics;
using Microsoft.AspNetCore.Routing.Patterns;
namespace Microsoft.AspNetCore.Routing.Matching
{
internal abstract class MatchProcessorFactory
{
public abstract MatchProcessor Create(string parameterName, string value, bool optional);
public abstract MatchProcessor Create(string parameterName, IRouteConstraint value, bool optional);
public abstract MatchProcessor Create(string parameterName, MatchProcessor value, bool optional);
public MatchProcessor Create(RoutePatternParameterPart parameter, RoutePatternConstraintReference reference)
{
if (reference == null)
{
throw new ArgumentNullException(nameof(reference));
}
Debug.Assert(reference.MatchProcessor != null || reference.Constraint != null || reference.Content != null);
if (reference.MatchProcessor != null)
{
return Create(parameter?.Name, reference.MatchProcessor, parameter?.IsOptional ?? false);
}
if (reference.Constraint != null)
{
return Create(parameter?.Name, reference.Constraint, parameter?.IsOptional ?? false);
}
if (reference.Content != null)
{
return Create(parameter?.Name, reference.Content, parameter?.IsOptional ?? false);
}
// Unreachable
throw new NotSupportedException();
}
}
}

View File

@ -1,44 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing.Matching
{
internal class OptionalMatchProcessor : MatchProcessor
{
private readonly MatchProcessor _innerMatchProcessor;
public OptionalMatchProcessor(MatchProcessor innerMatchProcessor)
{
_innerMatchProcessor = innerMatchProcessor;
}
public string ParameterName { get; private set; }
public override void Initialize(string parameterName, string constraintArgument)
{
ParameterName = parameterName;
_innerMatchProcessor.Initialize(parameterName, constraintArgument);
}
public override bool ProcessInbound(HttpContext httpContext, RouteValueDictionary values)
{
return Process(httpContext, values);
}
public override bool ProcessOutbound(HttpContext httpContext, RouteValueDictionary values)
{
return Process(httpContext, values);
}
private bool Process(HttpContext httpContext, RouteValueDictionary values)
{
if (values.TryGetValue(ParameterName, out var value))
{
return _innerMatchProcessor.ProcessInbound(httpContext, values);
}
return true;
}
}
}

View File

@ -1,61 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing.Matching
{
internal class RouteConstraintMatchProcessor : MatchProcessor
{
public RouteConstraintMatchProcessor(string parameterName, IRouteConstraint constraint)
{
ParameterName = parameterName;
Constraint = constraint;
}
public string ParameterName { get; }
public IRouteConstraint Constraint { get; }
public override bool ProcessInbound(HttpContext httpContext, RouteValueDictionary values)
{
if (httpContext == null)
{
throw new ArgumentNullException(nameof(httpContext));
}
if (values == null)
{
throw new ArgumentNullException(nameof(values));
}
return Constraint.Match(
httpContext,
NullRouter.Instance,
ParameterName,
values,
RouteDirection.IncomingRequest);
}
public override bool ProcessOutbound(HttpContext httpContext, RouteValueDictionary values)
{
if (httpContext == null)
{
throw new ArgumentNullException(nameof(httpContext));
}
if (values == null)
{
throw new ArgumentNullException(nameof(values));
}
return Constraint.Match(
httpContext,
NullRouter.Instance,
ParameterName,
values,
RouteDirection.UrlGeneration);
}
}
}

View File

@ -0,0 +1,60 @@
// 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.Diagnostics;
using Microsoft.AspNetCore.Routing.Patterns;
namespace Microsoft.AspNetCore.Routing
{
/// <summary>
/// Defines an abstraction for resolving inline parameter policies as instances of <see cref="IParameterPolicy"/>.
/// </summary>
public abstract class ParameterPolicyFactory
{
/// <summary>
/// Creates a parameter policy.
/// </summary>
/// <param name="parameter">The parameter the parameter policy is being created for.</param>
/// <param name="inlineText">The inline text to resolve.</param>
/// <returns>The <see cref="IParameterPolicy"/> for the parameter.</returns>
public abstract IParameterPolicy Create(RoutePatternParameterPart parameter, string inlineText);
/// <summary>
/// Creates a parameter policy.
/// </summary>
/// <param name="parameter">The parameter the parameter policy is being created for.</param>
/// <param name="parameterPolicy">An existing parameter policy.</param>
/// <returns>The <see cref="IParameterPolicy"/> for the parameter.</returns>
public abstract IParameterPolicy Create(RoutePatternParameterPart parameter, IParameterPolicy parameterPolicy);
/// <summary>
/// Creates a parameter policy.
/// </summary>
/// <param name="parameter">The parameter the parameter policy is being created for.</param>
/// <param name="reference">The reference to resolve.</param>
/// <returns>The <see cref="IParameterPolicy"/> for the parameter.</returns>
public IParameterPolicy Create(RoutePatternParameterPart parameter, RoutePatternParameterPolicyReference reference)
{
if (reference == null)
{
throw new ArgumentNullException(nameof(reference));
}
Debug.Assert(reference.ParameterPolicy != null || reference.Content != null);
if (reference.ParameterPolicy != null)
{
return Create(parameter, reference.ParameterPolicy);
}
if (reference.Content != null)
{
return Create(parameter, reference.Content);
}
// Unreachable
throw new NotSupportedException();
}
}
}

View File

@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
if (parameter.Length == 0)
{
return new RoutePatternParameterPart(string.Empty, null, RoutePatternParameterKind.Standard, Array.Empty<RoutePatternConstraintReference>());
return new RoutePatternParameterPart(string.Empty, null, RoutePatternParameterKind.Standard, Array.Empty<RoutePatternParameterPolicyReference>());
}
var startIndex = 0;
@ -91,17 +91,17 @@ namespace Microsoft.AspNetCore.Routing.Patterns
parameterName,
defaultValue,
parameterKind,
parseResults.Constraints.ToArray(),
parseResults.ParameterPolicies.ToArray(),
encodeSlashes);
}
private static ConstraintParseResults ParseConstraints(
private static ParameterPolicyParseResults ParseConstraints(
string text,
string parameterName,
int currentIndex,
int endIndex)
{
var constraints = new List<RoutePatternConstraintReference>();
var constraints = new List<RoutePatternParameterPolicyReference>();
var state = ParseState.Start;
var startIndex = currentIndex;
do
@ -134,7 +134,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
case null:
state = ParseState.End;
var constraintText = text.Substring(startIndex, currentIndex - startIndex);
constraints.Add(RoutePatternFactory.Constraint(constraintText));
constraints.Add(RoutePatternFactory.ParameterPolicy(constraintText));
break;
case ')':
// Only consume a ')' token if
@ -148,18 +148,18 @@ namespace Microsoft.AspNetCore.Routing.Patterns
case null:
state = ParseState.End;
constraintText = text.Substring(startIndex, currentIndex - startIndex + 1);
constraints.Add(RoutePatternFactory.Constraint(constraintText));
constraints.Add(RoutePatternFactory.ParameterPolicy(constraintText));
break;
case ':':
state = ParseState.Start;
constraintText = text.Substring(startIndex, currentIndex - startIndex + 1);
constraints.Add(RoutePatternFactory.Constraint(constraintText));
constraints.Add(RoutePatternFactory.ParameterPolicy(constraintText));
startIndex = currentIndex + 1;
break;
case '=':
state = ParseState.End;
constraintText = text.Substring(startIndex, currentIndex - startIndex + 1);
constraints.Add(RoutePatternFactory.Constraint(constraintText));
constraints.Add(RoutePatternFactory.ParameterPolicy(constraintText));
break;
}
break;
@ -174,7 +174,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
if (indexOfClosingParantheses == -1)
{
constraintText = text.Substring(startIndex, currentIndex - startIndex);
constraints.Add(RoutePatternFactory.Constraint(constraintText));
constraints.Add(RoutePatternFactory.ParameterPolicy(constraintText));
if (currentChar == ':')
{
@ -203,14 +203,14 @@ namespace Microsoft.AspNetCore.Routing.Patterns
var constraintText = text.Substring(startIndex, currentIndex - startIndex);
if (constraintText.Length > 0)
{
constraints.Add(RoutePatternFactory.Constraint(constraintText));
constraints.Add(RoutePatternFactory.ParameterPolicy(constraintText));
}
break;
case ':':
constraintText = text.Substring(startIndex, currentIndex - startIndex);
if (constraintText.Length > 0)
{
constraints.Add(RoutePatternFactory.Constraint(constraintText));
constraints.Add(RoutePatternFactory.ParameterPolicy(constraintText));
}
startIndex = currentIndex + 1;
break;
@ -222,7 +222,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
constraintText = text.Substring(startIndex, currentIndex - startIndex);
if (constraintText.Length > 0)
{
constraints.Add(RoutePatternFactory.Constraint(constraintText));
constraints.Add(RoutePatternFactory.ParameterPolicy(constraintText));
}
currentIndex--;
break;
@ -234,7 +234,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
} while (state != ParseState.End);
return new ConstraintParseResults(currentIndex, constraints);
return new ParameterPolicyParseResults(currentIndex, constraints);
}
private enum ParseState
@ -245,16 +245,16 @@ namespace Microsoft.AspNetCore.Routing.Patterns
End
}
private readonly struct ConstraintParseResults
private readonly struct ParameterPolicyParseResults
{
public readonly int CurrentIndex;
public readonly IReadOnlyList<RoutePatternConstraintReference> Constraints;
public readonly IReadOnlyList<RoutePatternParameterPolicyReference> ParameterPolicies;
public ConstraintParseResults(int currentIndex, IReadOnlyList<RoutePatternConstraintReference> constraints)
public ParameterPolicyParseResults(int currentIndex, IReadOnlyList<RoutePatternParameterPolicyReference> parameterPolicies)
{
CurrentIndex = currentIndex;
Constraints = constraints;
ParameterPolicies = parameterPolicies;
}
}
}

View File

@ -22,18 +22,18 @@ namespace Microsoft.AspNetCore.Routing.Patterns
internal RoutePattern(
string rawText,
IReadOnlyDictionary<string, object> defaults,
IReadOnlyDictionary<string, IReadOnlyList<RoutePatternConstraintReference>> constraints,
IReadOnlyDictionary<string, IReadOnlyList<RoutePatternParameterPolicyReference>> parameterPolicies,
IReadOnlyList<RoutePatternParameterPart> parameters,
IReadOnlyList<RoutePatternPathSegment> pathSegments)
{
Debug.Assert(defaults != null);
Debug.Assert(constraints != null);
Debug.Assert(parameterPolicies != null);
Debug.Assert(parameters != null);
Debug.Assert(pathSegments != null);
RawText = rawText;
Defaults = defaults;
Constraints = constraints;
ParameterPolicies = parameterPolicies;
Parameters = parameters;
PathSegments = pathSegments;
@ -48,10 +48,10 @@ namespace Microsoft.AspNetCore.Routing.Patterns
public IReadOnlyDictionary<string, object> Defaults { get; }
/// <summary>
/// Gets the set of constraint references for the route pattern.
/// The keys of <see cref="Constraints"/> are the route parameter names.
/// Gets the set of parameter policy references for the route pattern.
/// The keys of <see cref="ParameterPolicies"/> are the route parameter names.
/// </summary>
public IReadOnlyDictionary<string, IReadOnlyList<RoutePatternConstraintReference>> Constraints { get; }
public IReadOnlyDictionary<string, IReadOnlyList<RoutePatternParameterPolicyReference>> ParameterPolicies { get; }
/// <summary>
/// Gets the precedence value of the route pattern for URL matching.

View File

@ -1,51 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Diagnostics;
using Microsoft.AspNetCore.Routing.Matching;
namespace Microsoft.AspNetCore.Routing.Patterns
{
/// <summary>
/// The parsed representation of a constraint in a <see cref="RoutePattern"/> parameter. Instances
/// of <see cref="RoutePatternConstraintReference"/> are immutable.
/// </summary>
[DebuggerDisplay("{DebuggerToString()}")]
public sealed class RoutePatternConstraintReference
{
internal RoutePatternConstraintReference(string content)
{
Content = content;
}
internal RoutePatternConstraintReference(IRouteConstraint constraint)
{
Constraint = constraint;
}
internal RoutePatternConstraintReference(MatchProcessor matchProcessor)
{
MatchProcessor = matchProcessor;
}
/// <summary>
/// Gets the constraint text.
/// </summary>
public string Content { get; }
/// <summary>
/// Gets a pre-existing <see cref="IRouteConstraint"/> that was used to construct this reference.
/// </summary>
public IRouteConstraint Constraint { get; }
/// <summary>
/// Gets a pre-existing <see cref="Matching.MatchProcessor"/> that was used to construct this reference.
/// </summary>
public MatchProcessor MatchProcessor { get; }
private string DebuggerToString()
{
return Content;
}
}
}

View File

@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
/// <summary>
/// Creates a <see cref="RoutePattern"/> from its string representation along
/// with provided default values and constraints.
/// with provided default values and parameter policies.
/// </summary>
/// <param name="pattern">The route pattern string to parse.</param>
/// <param name="defaults">
@ -41,13 +41,13 @@ namespace Microsoft.AspNetCore.Routing.Patterns
/// The provided object will be converted to key-value pairs using <see cref="RouteValueDictionary"/>
/// and then merged into the parsed route pattern.
/// </param>
/// <param name="constraints">
/// Additional constraints to associated with the route pattern. May be null.
/// <param name="parameterPolicies">
/// Additional parameter policies to associated with the route pattern. May be null.
/// The provided object will be converted to key-value pairs using <see cref="RouteValueDictionary"/>
/// and then merged into the parsed route pattern.
/// </param>
/// <returns>The <see cref="RoutePattern"/>.</returns>
public static RoutePattern Parse(string pattern, object defaults, object constraints)
public static RoutePattern Parse(string pattern, object defaults, object parameterPolicies)
{
if (pattern == null)
{
@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
}
var original = RoutePatternParser.Parse(pattern);
return Pattern(original.RawText, defaults, constraints, original.PathSegments);
return Pattern(original.RawText, defaults, parameterPolicies, original.PathSegments);
}
/// <summary>
@ -91,15 +91,15 @@ namespace Microsoft.AspNetCore.Routing.Patterns
/// <summary>
/// Creates a <see cref="RoutePattern"/> from a collection of segments along
/// with provided default values and constraints.
/// with provided default values and parameter policies.
/// </summary>
/// <param name="defaults">
/// Additional default values to associated with the route pattern. May be null.
/// The provided object will be converted to key-value pairs using <see cref="RouteValueDictionary"/>
/// and then merged into the route pattern.
/// </param>
/// <param name="constraints">
/// Additional constraints to associated with the route pattern. May be null.
/// <param name="parameterPolicies">
/// Additional parameter policies to associated with the route pattern. May be null.
/// The provided object will be converted to key-value pairs using <see cref="RouteValueDictionary"/>
/// and then merged into the route pattern.
/// </param>
@ -107,7 +107,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
/// <returns>The <see cref="RoutePattern"/>.</returns>
public static RoutePattern Pattern(
object defaults,
object constraints,
object parameterPolicies,
IEnumerable<RoutePatternPathSegment> segments)
{
if (segments == null)
@ -115,12 +115,12 @@ namespace Microsoft.AspNetCore.Routing.Patterns
throw new ArgumentNullException(nameof(segments));
}
return PatternCore(null, new RouteValueDictionary(defaults), new RouteValueDictionary(constraints), segments);
return PatternCore(null, new RouteValueDictionary(defaults), new RouteValueDictionary(parameterPolicies), segments);
}
/// <summary>
/// Creates a <see cref="RoutePattern"/> from a collection of segments along
/// with provided default values and constraints.
/// with provided default values and parameter policies.
/// </summary>
/// <param name="rawText">The raw text to associate with the route pattern.</param>
/// <param name="defaults">
@ -128,8 +128,8 @@ namespace Microsoft.AspNetCore.Routing.Patterns
/// The provided object will be converted to key-value pairs using <see cref="RouteValueDictionary"/>
/// and then merged into the route pattern.
/// </param>
/// <param name="constraints">
/// Additional constraints to associated with the route pattern. May be null.
/// <param name="parameterPolicies">
/// Additional parameter policies to associated with the route pattern. May be null.
/// The provided object will be converted to key-value pairs using <see cref="RouteValueDictionary"/>
/// and then merged into the route pattern.
/// </param>
@ -138,7 +138,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
public static RoutePattern Pattern(
string rawText,
object defaults,
object constraints,
object parameterPolicies,
IEnumerable<RoutePatternPathSegment> segments)
{
if (segments == null)
@ -146,7 +146,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
throw new ArgumentNullException(nameof(segments));
}
return PatternCore(rawText, new RouteValueDictionary(defaults), new RouteValueDictionary(constraints), segments);
return PatternCore(rawText, new RouteValueDictionary(defaults), new RouteValueDictionary(parameterPolicies), segments);
}
/// <summary>
@ -182,15 +182,15 @@ namespace Microsoft.AspNetCore.Routing.Patterns
/// <summary>
/// Creates a <see cref="RoutePattern"/> from a collection of segments along
/// with provided default values and constraints.
/// with provided default values and parameter policies.
/// </summary>
/// <param name="defaults">
/// Additional default values to associated with the route pattern. May be null.
/// The provided object will be converted to key-value pairs using <see cref="RouteValueDictionary"/>
/// and then merged into the route pattern.
/// </param>
/// <param name="constraints">
/// Additional constraints to associated with the route pattern. May be null.
/// <param name="parameterPolicies">
/// Additional parameter policies to associated with the route pattern. May be null.
/// The provided object will be converted to key-value pairs using <see cref="RouteValueDictionary"/>
/// and then merged into the route pattern.
/// </param>
@ -198,7 +198,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
/// <returns>The <see cref="RoutePattern"/>.</returns>
public static RoutePattern Pattern(
object defaults,
object constraints,
object parameterPolicies,
params RoutePatternPathSegment[] segments)
{
if (segments == null)
@ -206,12 +206,12 @@ namespace Microsoft.AspNetCore.Routing.Patterns
throw new ArgumentNullException(nameof(segments));
}
return PatternCore(null, new RouteValueDictionary(defaults), new RouteValueDictionary(constraints), segments);
return PatternCore(null, new RouteValueDictionary(defaults), new RouteValueDictionary(parameterPolicies), segments);
}
/// <summary>
/// Creates a <see cref="RoutePattern"/> from a collection of segments along
/// with provided default values and constraints.
/// with provided default values and parameter policies.
/// </summary>
/// <param name="rawText">The raw text to associate with the route pattern.</param>
/// <param name="defaults">
@ -219,8 +219,8 @@ namespace Microsoft.AspNetCore.Routing.Patterns
/// The provided object will be converted to key-value pairs using <see cref="RouteValueDictionary"/>
/// and then merged into the route pattern.
/// </param>
/// <param name="constraints">
/// Additional constraints to associated with the route pattern. May be null.
/// <param name="parameterPolicies">
/// Additional parameter policies to associated with the route pattern. May be null.
/// The provided object will be converted to key-value pairs using <see cref="RouteValueDictionary"/>
/// and then merged into the route pattern.
/// </param>
@ -229,7 +229,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
public static RoutePattern Pattern(
string rawText,
object defaults,
object constraints,
object parameterPolicies,
params RoutePatternPathSegment[] segments)
{
if (segments == null)
@ -237,21 +237,21 @@ namespace Microsoft.AspNetCore.Routing.Patterns
throw new ArgumentNullException(nameof(segments));
}
return PatternCore(rawText, new RouteValueDictionary(defaults), new RouteValueDictionary(constraints), segments);
return PatternCore(rawText, new RouteValueDictionary(defaults), new RouteValueDictionary(parameterPolicies), segments);
}
private static RoutePattern PatternCore(
string rawText,
IDictionary<string, object> defaults,
IDictionary<string, object> constraints,
IDictionary<string, object> parameterPolicies,
IEnumerable<RoutePatternPathSegment> segments)
{
// We want to merge the segment data with the 'out of line' defaults and constraints.
// We want to merge the segment data with the 'out of line' defaults and parameter policies.
//
// This means that for parameters that have 'out of line' defaults we will modify
// the parameter to contain the default (same story for constraints).
// the parameter to contain the default (same story for parameter policies).
//
// We also maintain a collection of defaults and constraints that will also
// We also maintain a collection of defaults and parameter policies that will also
// contain the values that don't match a parameter.
//
// It's important that these two views of the data are consistent. We don't want
@ -266,14 +266,16 @@ namespace Microsoft.AspNetCore.Routing.Patterns
}
}
var updatedConstraints = new Dictionary<string, List<RoutePatternConstraintReference>>(StringComparer.OrdinalIgnoreCase);
if (constraints != null)
var updatedParameterPolicies = new Dictionary<string, List<RoutePatternParameterPolicyReference>>(StringComparer.OrdinalIgnoreCase);
if (parameterPolicies != null)
{
foreach (var kvp in constraints)
foreach (var kvp in parameterPolicies)
{
updatedConstraints.Add(kvp.Key, new List<RoutePatternConstraintReference>()
updatedParameterPolicies.Add(kvp.Key, new List<RoutePatternParameterPolicyReference>()
{
Constraint(kvp.Value),
kvp.Value is IParameterPolicy parameterPolicy
? ParameterPolicy(parameterPolicy)
: Constraint(kvp.Value), // Constraint will convert string values into regex constraints
});
}
}
@ -297,7 +299,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
return new RoutePattern(
rawText,
updatedDefaults,
updatedConstraints.ToDictionary(kvp => kvp.Key, kvp => (IReadOnlyList<RoutePatternConstraintReference>)kvp.Value.ToArray()),
updatedParameterPolicies.ToDictionary(kvp => kvp.Key, kvp => (IReadOnlyList<RoutePatternParameterPolicyReference>)kvp.Value.ToArray()),
parameters,
updatedSegments);
@ -361,20 +363,20 @@ namespace Microsoft.AspNetCore.Routing.Patterns
updatedDefaults.Add(parameter.Name, parameter.Default);
}
if (!updatedConstraints.TryGetValue(parameter.Name, out var parameterConstraints) &&
parameter.Constraints.Count > 0)
if (!updatedParameterPolicies.TryGetValue(parameter.Name, out var parameterConstraints) &&
parameter.ParameterPolicies.Count > 0)
{
parameterConstraints = new List<RoutePatternConstraintReference>();
updatedConstraints.Add(parameter.Name, parameterConstraints);
parameterConstraints = new List<RoutePatternParameterPolicyReference>();
updatedParameterPolicies.Add(parameter.Name, parameterConstraints);
}
if (parameter.Constraints.Count > 0)
if (parameter.ParameterPolicies.Count > 0)
{
parameterConstraints.AddRange(parameter.Constraints);
parameterConstraints.AddRange(parameter.ParameterPolicies);
}
if (Equals(parameter.Default, @default)
&& parameter.Constraints.Count == 0
&& parameter.ParameterPolicies.Count == 0
&& (parameterConstraints?.Count ?? 0) == 0)
{
// Part has not changed
@ -385,11 +387,10 @@ namespace Microsoft.AspNetCore.Routing.Patterns
parameter.Name,
@default,
parameter.ParameterKind,
(IEnumerable<RoutePatternConstraintReference>)parameterConstraints ?? Array.Empty<RoutePatternConstraintReference>(),
(IEnumerable<RoutePatternParameterPolicyReference>)parameterConstraints ?? Array.Empty<RoutePatternParameterPolicyReference>(),
parameter.EncodeSlashes);
}
}
/// <summary>
/// Creates a <see cref="RoutePatternPathSegment"/> from the provided collection
/// of parts.
@ -495,7 +496,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
parameterName: parameterName,
@default: null,
parameterKind: RoutePatternParameterKind.Standard,
constraints: Array.Empty<RoutePatternConstraintReference>());
parameterPolicies: Array.Empty<RoutePatternParameterPolicyReference>());
}
/// <summary>
@ -521,7 +522,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
parameterName: parameterName,
@default: @default,
parameterKind: RoutePatternParameterKind.Standard,
constraints: Array.Empty<RoutePatternConstraintReference>());
parameterPolicies: Array.Empty<RoutePatternParameterPolicyReference>());
}
/// <summary>
@ -556,23 +557,23 @@ namespace Microsoft.AspNetCore.Routing.Patterns
parameterName: parameterName,
@default: @default,
parameterKind: parameterKind,
constraints: Array.Empty<RoutePatternConstraintReference>());
parameterPolicies: Array.Empty<RoutePatternParameterPolicyReference>());
}
/// <summary>
/// Creates a <see cref="RoutePatternParameterPart"/> from the provided parameter name
/// and default value, parameter kind, and constraints.
/// and default value, parameter kind, and parameter policies.
/// </summary>
/// <param name="parameterName">The parameter name.</param>
/// <param name="default">The parameter default value. May be <c>null</c>.</param>
/// <param name="parameterKind">The parameter kind.</param>
/// <param name="constraints">The constraints to associated with the parameter.</param>
/// <param name="parameterPolicies">The parameter policies to associated with the parameter.</param>
/// <returns>The <see cref="RoutePatternParameterPart"/>.</returns>
public static RoutePatternParameterPart ParameterPart(
string parameterName,
object @default,
RoutePatternParameterKind parameterKind,
IEnumerable<RoutePatternConstraintReference> constraints)
IEnumerable<RoutePatternParameterPolicyReference> parameterPolicies)
{
if (string.IsNullOrEmpty(parameterName))
{
@ -589,32 +590,32 @@ namespace Microsoft.AspNetCore.Routing.Patterns
throw new ArgumentNullException(Resources.TemplateRoute_OptionalCannotHaveDefaultValue, nameof(parameterKind));
}
if (constraints == null)
if (parameterPolicies == null)
{
throw new ArgumentNullException(nameof(constraints));
throw new ArgumentNullException(nameof(parameterPolicies));
}
return ParameterPartCore(
parameterName: parameterName,
@default: @default,
parameterKind: parameterKind,
constraints: constraints);
parameterPolicies: parameterPolicies);
}
/// <summary>
/// Creates a <see cref="RoutePatternParameterPart"/> from the provided parameter name
/// and default value, parameter kind, and constraints.
/// and default value, parameter kind, and parameter policies.
/// </summary>
/// <param name="parameterName">The parameter name.</param>
/// <param name="default">The parameter default value. May be <c>null</c>.</param>
/// <param name="parameterKind">The parameter kind.</param>
/// <param name="constraints">The constraints to associated with the parameter.</param>
/// <param name="parameterPolicies">The parameter policies to associated with the parameter.</param>
/// <returns>The <see cref="RoutePatternParameterPart"/>.</returns>
public static RoutePatternParameterPart ParameterPart(
string parameterName,
object @default,
RoutePatternParameterKind parameterKind,
params RoutePatternConstraintReference[] constraints)
params RoutePatternParameterPolicyReference[] parameterPolicies)
{
if (string.IsNullOrEmpty(parameterName))
{
@ -631,141 +632,146 @@ namespace Microsoft.AspNetCore.Routing.Patterns
throw new ArgumentNullException(Resources.TemplateRoute_OptionalCannotHaveDefaultValue, nameof(parameterKind));
}
if (constraints == null)
if (parameterPolicies == null)
{
throw new ArgumentNullException(nameof(constraints));
throw new ArgumentNullException(nameof(parameterPolicies));
}
return ParameterPartCore(
parameterName: parameterName,
@default: @default,
parameterKind: parameterKind,
constraints: constraints);
parameterPolicies: parameterPolicies);
}
private static RoutePatternParameterPart ParameterPartCore(
string parameterName,
object @default,
RoutePatternParameterKind parameterKind,
IEnumerable<RoutePatternConstraintReference> constraints)
IEnumerable<RoutePatternParameterPolicyReference> parameterPolicies)
{
return ParameterPartCore(parameterName, @default, parameterKind, constraints, encodeSlashes: true);
return ParameterPartCore(parameterName, @default, parameterKind, parameterPolicies, encodeSlashes: true);
}
private static RoutePatternParameterPart ParameterPartCore(
string parameterName,
object @default,
RoutePatternParameterKind parameterKind,
IEnumerable<RoutePatternConstraintReference> constraints,
IEnumerable<RoutePatternParameterPolicyReference> parameterPolicies,
bool encodeSlashes)
{
return new RoutePatternParameterPart(
parameterName,
@default,
parameterKind,
constraints.ToArray(),
parameterPolicies.ToArray(),
encodeSlashes);
}
/// <summary>
/// Creates a <see cref="RoutePatternConstraintReference"/> from the provided object.
/// Creates a <see cref="RoutePatternParameterPolicyReference"/> from the provided contraint.
/// </summary>
/// <param name="constraint">
/// The constraint object, which must be of type <see cref="IRouteConstraint"/>,
/// <see cref="MatcherPolicy"/>, or <see cref="String"/>. If the constraint object
/// is a <see cref="String"/> it will be tranformed into an instance of
/// <see cref="RegexRouteConstraint"/>.
/// The constraint object, which must be of type <see cref="IRouteConstraint"/>
/// or <see cref="string"/>. If the constraint object is a <see cref="string"/>
/// then it will be tranformed into an instance of <see cref="RegexRouteConstraint"/>.
/// </param>
/// <returns>The <see cref="RoutePatternConstraintReference"/>.</returns>
public static RoutePatternConstraintReference Constraint(object constraint)
/// <returns>The <see cref="RoutePatternParameterPolicyReference"/>.</returns>
public static RoutePatternParameterPolicyReference Constraint(object constraint)
{
// Similar to RouteConstraintBuilder
if (constraint is IRouteConstraint routeConstraint)
if (constraint is IRouteConstraint policy)
{
return ConstraintCore(routeConstraint);
}
else if (constraint is MatchProcessor matchProcessor)
{
return ConstraintCore(matchProcessor);
return ParameterPolicyCore(policy);
}
else if (constraint is string content)
{
return ConstraintCore(new RegexRouteConstraint("^(" + content + ")$"));
return ParameterPolicyCore(new RegexRouteConstraint("^(" + content + ")$"));
}
else
{
throw new InvalidOperationException(Resources.FormatRoutePattern_InvalidConstraintReference(
constraint ?? "null",
typeof(IRouteConstraint),
typeof(MatchProcessor)));
typeof(IRouteConstraint)));
}
}
/// <summary>
/// Creates a <see cref="RoutePatternConstraintReference"/> from the provided object.
/// Creates a <see cref="RoutePatternParameterPolicyReference"/> from the provided constraint.
/// </summary>
/// <param name="constraint">
/// The constraint object.
/// </param>
/// <returns>The <see cref="RoutePatternConstraintReference"/>.</returns>
public static RoutePatternConstraintReference Constraint(IRouteConstraint constraint)
/// <returns>The <see cref="RoutePatternParameterPolicyReference"/>.</returns>
public static RoutePatternParameterPolicyReference Constraint(IRouteConstraint constraint)
{
if (constraint == null)
{
throw new ArgumentNullException(nameof(constraint));
}
return ConstraintCore(constraint);
return ParameterPolicyCore(constraint);
}
/// <summary>
/// Creates a <see cref="RoutePatternConstraintReference"/> from the provided object.
/// </summary>
/// <param name="matchProcessor">
/// The match processor object.
/// </param>
/// <returns>The <see cref="RoutePatternConstraintReference"/>.</returns>
public static RoutePatternConstraintReference Constraint(MatchProcessor matchProcessor)
{
if (matchProcessor == null)
{
throw new ArgumentNullException(nameof(matchProcessor));
}
return ConstraintCore(matchProcessor);
}
/// <summary>
/// Creates a <see cref="RoutePatternConstraintReference"/> from the provided object.
/// Creates a <see cref="RoutePatternParameterPolicyReference"/> from the provided constraint.
/// </summary>
/// <param name="constraint">
/// The constraint text, which will be tranformed into an instance of
/// <see cref="RegexRouteConstraint"/>.
/// The constraint text, which will be resolved by <see cref="ParameterPolicyFactory"/>.
/// </param>
/// <returns>The <see cref="RoutePatternConstraintReference"/>.</returns>
public static RoutePatternConstraintReference Constraint(string constraint)
/// <returns>The <see cref="RoutePatternParameterPolicyReference"/>.</returns>
public static RoutePatternParameterPolicyReference Constraint(string constraint)
{
if (string.IsNullOrEmpty(constraint))
{
throw new ArgumentException(Resources.Argument_NullOrEmpty, nameof(constraint));
}
return ConstraintCore(constraint);
return ParameterPolicyCore(constraint);
}
private static RoutePatternConstraintReference ConstraintCore(string constraint)
/// <summary>
/// Creates a <see cref="RoutePatternParameterPolicyReference"/> from the provided object.
/// </summary>
/// <param name="parameterPolicy">
/// The parameter policy object.
/// </param>
/// <returns>The <see cref="RoutePatternParameterPolicyReference"/>.</returns>
public static RoutePatternParameterPolicyReference ParameterPolicy(IParameterPolicy parameterPolicy)
{
return new RoutePatternConstraintReference(constraint);
if (parameterPolicy == null)
{
throw new ArgumentNullException(nameof(parameterPolicy));
}
return ParameterPolicyCore(parameterPolicy);
}
private static RoutePatternConstraintReference ConstraintCore(IRouteConstraint constraint)
/// <summary>
/// Creates a <see cref="RoutePatternParameterPolicyReference"/> from the provided object.
/// </summary>
/// <param name="parameterPolicy">
/// The parameter policy text, which will be resolved by <see cref="ParameterPolicyFactory"/>.
/// </param>
/// <returns>The <see cref="RoutePatternParameterPolicyReference"/>.</returns>
public static RoutePatternParameterPolicyReference ParameterPolicy(string parameterPolicy)
{
return new RoutePatternConstraintReference(constraint);
if (string.IsNullOrEmpty(parameterPolicy))
{
throw new ArgumentException(Resources.Argument_NullOrEmpty, nameof(parameterPolicy));
}
return ParameterPolicyCore(parameterPolicy);
}
private static RoutePatternConstraintReference ConstraintCore(MatchProcessor matchProcessor)
private static RoutePatternParameterPolicyReference ParameterPolicyCore(string parameterPolicy)
{
return new RoutePatternConstraintReference(matchProcessor);
return new RoutePatternParameterPolicyReference(parameterPolicy);
}
private static RoutePatternParameterPolicyReference ParameterPolicyCore(IParameterPolicy parameterPolicy)
{
return new RoutePatternParameterPolicyReference(parameterPolicy);
}
}
}

View File

@ -18,8 +18,8 @@ namespace Microsoft.AspNetCore.Routing.Patterns
string parameterName,
object @default,
RoutePatternParameterKind parameterKind,
RoutePatternConstraintReference[] constraints)
: this(parameterName, @default, parameterKind, constraints, encodeSlashes: true)
RoutePatternParameterPolicyReference[] parameterPolicies)
: this(parameterName, @default, parameterKind, parameterPolicies, encodeSlashes: true)
{
}
@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
string parameterName,
object @default,
RoutePatternParameterKind parameterKind,
RoutePatternConstraintReference[] constraints,
RoutePatternParameterPolicyReference[] parameterPolicies,
bool encodeSlashes)
: base(RoutePatternPartKind.Parameter)
{
@ -36,14 +36,14 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Name = parameterName;
Default = @default;
ParameterKind = parameterKind;
Constraints = constraints;
ParameterPolicies = parameterPolicies;
EncodeSlashes = encodeSlashes;
}
/// <summary>
/// Gets the list of constraints associated with this parameter.
/// Gets the list of parameter policies associated with this parameter.
/// </summary>
public IReadOnlyList<RoutePatternConstraintReference> Constraints { get; }
public IReadOnlyList<RoutePatternParameterPolicyReference> ParameterPolicies { get; }
/// <summary>
/// Gets the value indicating if slashes in current parameter's value should be encoded.
@ -89,10 +89,10 @@ namespace Microsoft.AspNetCore.Routing.Patterns
builder.Append(Name);
foreach (var constraint in Constraints)
foreach (var constraint in ParameterPolicies)
{
builder.Append(":");
builder.Append(constraint.Constraint);
builder.Append(constraint.ParameterPolicy);
}
if (Default != null)

View File

@ -0,0 +1,41 @@
// 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.Diagnostics;
using Microsoft.AspNetCore.Routing.Matching;
namespace Microsoft.AspNetCore.Routing.Patterns
{
/// <summary>
/// The parsed representation of a policy in a <see cref="RoutePattern"/> parameter. Instances
/// of <see cref="RoutePatternParameterPolicyReference"/> are immutable.
/// </summary>
[DebuggerDisplay("{DebuggerToString()}")]
public sealed class RoutePatternParameterPolicyReference
{
internal RoutePatternParameterPolicyReference(string content)
{
Content = content;
}
internal RoutePatternParameterPolicyReference(IParameterPolicy parameterPolicy)
{
ParameterPolicy = parameterPolicy;
}
/// <summary>
/// Gets the constraint text.
/// </summary>
public string Content { get; }
/// <summary>
/// Gets a pre-existing <see cref="IParameterPolicy"/> that was used to construct this reference.
/// </summary>
public IParameterPolicy ParameterPolicy { get; }
private string DebuggerToString()
{
return Content;
}
}
}

View File

@ -467,10 +467,10 @@ namespace Microsoft.AspNetCore.Routing
}
/// <summary>
/// Invalid constraint '{0}'. A constraint must be of type 'string', '{1}', or '{2}'.
/// Invalid constraint '{0}'. A constraint must be of type 'string' or '{1}'.
/// </summary>
internal static string FormatRoutePattern_InvalidConstraintReference(object p0, object p1, object p2)
=> string.Format(CultureInfo.CurrentCulture, GetString("RoutePattern_InvalidConstraintReference"), p0, p1, p2);
internal static string FormatRoutePattern_InvalidConstraintReference(object p0, object p1)
=> string.Format(CultureInfo.CurrentCulture, GetString("RoutePattern_InvalidConstraintReference"), p0, p1);
/// <summary>
/// Invalid constraint '{0}' for parameter '{1}'. A constraint must be of type 'string', '{2}', or '{3}'.

View File

@ -214,7 +214,7 @@
<value>The constraint entry '{0}' - '{1}' must have a string value or be of a type which implements '{2}'.</value>
</data>
<data name="RoutePattern_InvalidConstraintReference" xml:space="preserve">
<value>Invalid constraint '{0}'. A constraint must be of type 'string', '{1}', or '{2}'.</value>
<value>Invalid constraint '{0}'. A constraint must be of type 'string' or '{1}'.</value>
</data>
<data name="RoutePattern_InvalidParameterConstraintReference" xml:space="preserve">
<value>Invalid constraint '{0}' for parameter '{1}'. A constraint must be of type 'string', '{2}', or '{3}'.</value>

View File

@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Routing.Template
Constraint = constraint;
}
public InlineConstraint(RoutePatternConstraintReference other)
public InlineConstraint(RoutePatternParameterPolicyReference other)
{
if (other == null)
{

View File

@ -149,7 +149,7 @@ namespace Microsoft.AspNetCore.Routing.Template
Debug.Assert(parameterPart != null);
var digit = parameterPart.IsCatchAll ? 1 : 3;
if (parameterPart.Constraints.Count > 0)
if (parameterPart.ParameterPolicies.Count > 0)
{
digit++;
}
@ -220,7 +220,7 @@ namespace Microsoft.AspNetCore.Routing.Template
// If there is a route constraint for the parameter, reduce order by 1
// Constrained parameters end up with order 2, Constrained catch alls end up with order 4
if (parameterPart.Constraints.Count > 0)
if (parameterPart.ParameterPolicies.Count > 0)
{
digit--;
}

View File

@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Routing.Template
IsCatchAll = parameter.IsCatchAll;
IsOptional = parameter.IsOptional;
DefaultValue = parameter.Default;
InlineConstraints = parameter.Constraints?.Select(p => new InlineConstraint(p));
InlineConstraints = parameter.ParameterPolicies?.Select(p => new InlineConstraint(p));
}
else if (other.IsSeparator && other is RoutePatternSeparatorPart separator)
{
@ -118,7 +118,7 @@ namespace Microsoft.AspNetCore.Routing.Template
RoutePatternParameterKind.Optional :
RoutePatternParameterKind.Standard;
var constraints = InlineConstraints.Select(c => new RoutePatternConstraintReference(c.Constraint));
var constraints = InlineConstraints.Select(c => new RoutePatternParameterPolicyReference(c.Constraint));
return RoutePatternFactory.ParameterPart(Name, DefaultValue, kind, constraints);
}
}

View File

@ -154,7 +154,7 @@ namespace Microsoft.AspNetCore.Routing
{
return new MatcherEndpoint(
MatcherEndpoint.EmptyInvoker,
RoutePatternFactory.Parse(template, defaults, constraints: null),
RoutePatternFactory.Parse(template, defaults, parameterPolicies: null),
order,
EndpointMetadataCollection.Empty,
null);

View File

@ -1554,7 +1554,7 @@ namespace Microsoft.AspNetCore.Routing
var serviceProvider = services.BuildServiceProvider();
return new DefaultLinkGenerator(
new DefaultMatchProcessorFactory(options, serviceProvider),
new DefaultParameterPolicyFactory(options, serviceProvider),
new DefaultObjectPool<UriBuildingContext>(new UriBuilderContextPooledObjectPolicy()),
options,
NullLogger<DefaultLinkGenerator>.Instance,

View File

@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
{
var dataSource = new CompositeEndpointDataSource(Array.Empty<EndpointDataSource>());
return new DfaMatcherBuilder(
Mock.Of<MatchProcessorFactory>(),
Mock.Of<ParameterPolicyFactory>(),
Mock.Of<EndpointSelector>(),
policies);
}

View File

@ -1,320 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Globalization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Constraints;
using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit;
namespace Microsoft.AspNetCore.Routing.Matching
{
public class DefaultMatchProcessorFactoryTest
{
[Fact]
public void Create_ThrowsException_IfNoConstraintOrMatchProcessor_FoundInMap()
{
// Arrange
var factory = GetMatchProcessorFactory();
// Act
var exception = Assert.Throws<InvalidOperationException>(
() => factory.Create("id", @"notpresent(\d+)", optional: false));
// Assert
Assert.Equal(
$"The constraint reference 'notpresent' could not be resolved to a type. " +
$"Register the constraint type with '{typeof(RouteOptions)}.{nameof(RouteOptions.ConstraintMap)}'.",
exception.Message);
}
[Fact]
public void Create_ThrowsException_OnInvalidType()
{
// Arrange
var options = new RouteOptions();
options.ConstraintMap.Add("bad", typeof(string));
var services = new ServiceCollection();
services.AddTransient<EndsWithStringMatchProcessor>();
var factory = GetMatchProcessorFactory(options, services);
// Act
var exception = Assert.Throws<InvalidOperationException>(
() => factory.Create("id", @"bad", optional: false));
// Assert
Assert.Equal(
$"Invalid constraint type '{typeof(string)}' registered as 'bad'. " +
$"A constraint type must either implement '{typeof(IRouteConstraint)}', or inherit from '{typeof(MatchProcessor)}'.",
exception.Message);
}
[Fact]
public void Create_CreatesMatchProcessor_FromRoutePattern_String()
{
// Arrange
var factory = GetMatchProcessorFactory();
var parameter = RoutePatternFactory.ParameterPart(
"id",
@default: null,
parameterKind: RoutePatternParameterKind.Standard,
constraints: new[] { RoutePatternFactory.Constraint("int"), });
// Act
var matchProcessor = factory.Create(parameter, parameter.Constraints[0]);
// Assert
Assert.IsType<IntRouteConstraint>(Assert.IsType<RouteConstraintMatchProcessor>(matchProcessor).Constraint);
}
[Fact]
public void Create_CreatesMatchProcessor_FromRoutePattern_String_Optional()
{
// Arrange
var factory = GetMatchProcessorFactory();
var parameter = RoutePatternFactory.ParameterPart(
"id",
@default: null,
parameterKind: RoutePatternParameterKind.Optional,
constraints: new[] { RoutePatternFactory.Constraint("int"), });
// Act
var matchProcessor = factory.Create(parameter, parameter.Constraints[0]);
// Assert
Assert.IsType<OptionalMatchProcessor>(matchProcessor);
}
[Fact]
public void Create_CreatesMatchProcessor_FromRoutePattern_Constraint()
{
// Arrange
var factory = GetMatchProcessorFactory();
var parameter = RoutePatternFactory.ParameterPart(
"id",
@default: null,
parameterKind: RoutePatternParameterKind.Standard,
constraints: new[] { RoutePatternFactory.Constraint(new IntRouteConstraint()), });
// Act
var matchProcessor = factory.Create(parameter, parameter.Constraints[0]);
// Assert
Assert.IsType<IntRouteConstraint>(Assert.IsType<RouteConstraintMatchProcessor>(matchProcessor).Constraint);
}
[Fact]
public void Create_CreatesMatchProcessor_FromRoutePattern_Constraint_Optional()
{
// Arrange
var factory = GetMatchProcessorFactory();
var parameter = RoutePatternFactory.ParameterPart(
"id",
@default: null,
parameterKind: RoutePatternParameterKind.Optional,
constraints: new[] { RoutePatternFactory.Constraint(new IntRouteConstraint()), });
// Act
var matchProcessor = factory.Create(parameter, parameter.Constraints[0]);
// Assert
Assert.IsType<OptionalMatchProcessor>(matchProcessor);
}
[Fact]
public void Create_CreatesMatchProcessor_FromRoutePattern_MatchProcessor()
{
// Arrange
var factory = GetMatchProcessorFactory();
var parameter = RoutePatternFactory.ParameterPart(
"id",
@default: null,
parameterKind: RoutePatternParameterKind.Standard,
constraints: new[] { RoutePatternFactory.Constraint(new EndsWithStringMatchProcessor()), });
// Act
var matchProcessor = factory.Create(parameter, parameter.Constraints[0]);
// Assert
Assert.IsType<EndsWithStringMatchProcessor>(matchProcessor);
}
[Fact]
public void Create_CreatesMatchProcessor_FromRoutePattern_MatchProcessor_Optional()
{
// Arrange
var factory = GetMatchProcessorFactory();
var parameter = RoutePatternFactory.ParameterPart(
"id",
@default: null,
parameterKind: RoutePatternParameterKind.Optional,
constraints: new[] { RoutePatternFactory.Constraint(new EndsWithStringMatchProcessor()), });
// Act
var matchProcessor = factory.Create(parameter, parameter.Constraints[0]);
// Assert
Assert.IsType<OptionalMatchProcessor>(matchProcessor);
}
[Fact]
public void Create_CreatesMatchProcessor_FromConstraintText_AndRouteConstraint()
{
// Arrange
var factory = GetMatchProcessorFactory();
// Act
var matchProcessor = factory.Create("id", "int", optional: false);
// Assert
Assert.IsType<IntRouteConstraint>(Assert.IsType<RouteConstraintMatchProcessor>(matchProcessor).Constraint);
}
[Fact]
public void Create_CreatesMatchProcessor_FromConstraintText_AndRouteConstraint_Optional()
{
// Arrange
var factory = GetMatchProcessorFactory();
// Act
var matchProcessor = factory.Create("id", "int", optional: true);
// Assert
Assert.IsType<OptionalMatchProcessor>(matchProcessor);
}
[Fact]
public void Create_CreatesMatchProcessor_FromConstraintText_AndMatchProcesor()
{
// Arrange
var options = new RouteOptions();
options.ConstraintMap.Add("endsWith", typeof(EndsWithStringMatchProcessor));
var services = new ServiceCollection();
services.AddTransient<EndsWithStringMatchProcessor>();
var factory = GetMatchProcessorFactory(options, services);
// Act
var matchProcessor = factory.Create("id", "endsWith", optional: false);
// Assert
Assert.IsType<EndsWithStringMatchProcessor>(matchProcessor);
}
[Fact]
public void Create_CreatesMatchProcessor_FromConstraintText_AndMatchProcessor_Optional()
{
// Arrange
var options = new RouteOptions();
options.ConstraintMap.Add("endsWith", typeof(EndsWithStringMatchProcessor));
var services = new ServiceCollection();
services.AddTransient<EndsWithStringMatchProcessor>();
var factory = GetMatchProcessorFactory(options, services);
// Act
var matchProcessor = factory.Create("id", "endsWith", optional: true);
// Assert
Assert.IsType<OptionalMatchProcessor>(matchProcessor);
}
private DefaultMatchProcessorFactory GetMatchProcessorFactory(
RouteOptions options = null,
ServiceCollection services = null)
{
if (options == null)
{
options = new RouteOptions();
}
if (services == null)
{
services = new ServiceCollection();
}
return new DefaultMatchProcessorFactory(
Options.Create(options),
services.BuildServiceProvider());
}
private class TestRouteConstraint : IRouteConstraint
{
private TestRouteConstraint() { }
public HttpContext HttpContext { get; private set; }
public IRouter Route { get; private set; }
public string RouteKey { get; private set; }
public RouteValueDictionary Values { get; private set; }
public RouteDirection RouteDirection { get; private set; }
public static TestRouteConstraint Create()
{
return new TestRouteConstraint();
}
public bool Match(
HttpContext httpContext,
IRouter route,
string routeKey,
RouteValueDictionary values,
RouteDirection routeDirection)
{
HttpContext = httpContext;
Route = route;
RouteKey = routeKey;
Values = values;
RouteDirection = routeDirection;
return false;
}
}
private class EndsWithStringMatchProcessor : MatchProcessor
{
public string ParameterName { get; private set; }
public string ConstraintArgument { get; private set; }
public override void Initialize(string parameterName, string constraintArgument)
{
ParameterName = parameterName;
ConstraintArgument = constraintArgument;
}
public override bool ProcessInbound(HttpContext httpContext, RouteValueDictionary values)
{
return Process(values);
}
public override bool ProcessOutbound(HttpContext httpContext, RouteValueDictionary values)
{
return Process(values);
}
private bool Process(RouteValueDictionary values)
{
if (!values.TryGetValue(ParameterName, out var value) || value == null)
{
return false;
}
var valueString = Convert.ToString(value, CultureInfo.InvariantCulture);
var endsWith = valueString.EndsWith(ConstraintArgument, StringComparison.OrdinalIgnoreCase);
return endsWith;
}
}
}
}

View File

@ -0,0 +1,272 @@
// 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.Globalization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Constraints;
using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit;
namespace Microsoft.AspNetCore.Routing.Matching
{
public class DefaultParameterPolicyFactoryTest
{
[Fact]
public void Create_ThrowsException_IfNoConstraintOrParameterPolicy_FoundInMap()
{
// Arrange
var factory = GetParameterPolicyFactory();
// Act
var exception = Assert.Throws<InvalidOperationException>(
() => factory.Create(RoutePatternFactory.ParameterPart("id", @default: null, RoutePatternParameterKind.Optional), @"notpresent(\d+)"));
// Assert
Assert.Equal(
$"The constraint reference 'notpresent' could not be resolved to a type. " +
$"Register the constraint type with '{typeof(RouteOptions)}.{nameof(RouteOptions.ConstraintMap)}'.",
exception.Message);
}
[Fact]
public void Create_ThrowsException_OnInvalidType()
{
// Arrange
var options = new RouteOptions();
options.ConstraintMap.Add("bad", typeof(string));
var services = new ServiceCollection();
var factory = GetParameterPolicyFactory(options, services);
// Act
var exception = Assert.Throws<InvalidOperationException>(
() => factory.Create(RoutePatternFactory.ParameterPart("id"), @"bad"));
// Assert
Assert.Equal(
$"Invalid constraint type '{typeof(string)}' registered as 'bad'. " +
$"A constraint type must either implement '{typeof(IRouteConstraint)}', or inherit from '{typeof(IParameterPolicy)}'.",
exception.Message);
}
[Fact]
public void Create_CreatesParameterPolicy_FromRoutePattern_String()
{
// Arrange
var factory = GetParameterPolicyFactory();
var parameter = RoutePatternFactory.ParameterPart(
"id",
@default: null,
parameterKind: RoutePatternParameterKind.Standard,
parameterPolicies: new[] { RoutePatternFactory.Constraint("int"), });
// Act
var parameterPolicy = factory.Create(parameter, parameter.ParameterPolicies[0]);
// Assert
Assert.IsType<IntRouteConstraint>(parameterPolicy);
}
[Fact]
public void Create_CreatesParameterPolicy_FromRoutePattern_String_Optional()
{
// Arrange
var factory = GetParameterPolicyFactory();
var parameter = RoutePatternFactory.ParameterPart(
"id",
@default: null,
parameterKind: RoutePatternParameterKind.Optional,
parameterPolicies: new[] { RoutePatternFactory.Constraint("int"), });
// Act
var parameterPolicy = factory.Create(parameter, parameter.ParameterPolicies[0]);
// Assert
var optionalConstraint = Assert.IsType<OptionalRouteConstraint>(parameterPolicy);
Assert.IsType<IntRouteConstraint>(optionalConstraint.InnerConstraint);
}
[Fact]
public void Create_CreatesParameterPolicy_FromRoutePattern_Constraint()
{
// Arrange
var factory = GetParameterPolicyFactory();
var parameter = RoutePatternFactory.ParameterPart(
"id",
@default: null,
parameterKind: RoutePatternParameterKind.Standard,
parameterPolicies: new[] { RoutePatternFactory.ParameterPolicy(new IntRouteConstraint()), });
// Act
var parameterPolicy = factory.Create(parameter, parameter.ParameterPolicies[0]);
// Assert
Assert.IsType<IntRouteConstraint>(parameterPolicy);
}
[Fact]
public void Create_CreatesParameterPolicy_FromRoutePattern_Constraint_Optional()
{
// Arrange
var factory = GetParameterPolicyFactory();
var parameter = RoutePatternFactory.ParameterPart(
"id",
@default: null,
parameterKind: RoutePatternParameterKind.Optional,
parameterPolicies: new[] { RoutePatternFactory.ParameterPolicy(new IntRouteConstraint()), });
// Act
var parameterPolicy = factory.Create(parameter, parameter.ParameterPolicies[0]);
// Assert
var optionalConstraint = Assert.IsType<OptionalRouteConstraint>(parameterPolicy);
Assert.IsType<IntRouteConstraint>(optionalConstraint.InnerConstraint);
}
[Fact]
public void Create_CreatesParameterPolicy_FromRoutePattern_ParameterPolicy()
{
// Arrange
var factory = GetParameterPolicyFactory();
var parameter = RoutePatternFactory.ParameterPart(
"id",
@default: null,
parameterKind: RoutePatternParameterKind.Standard,
parameterPolicies: new[] { RoutePatternFactory.ParameterPolicy(new CustomParameterPolicy()), });
// Act
var parameterPolicy = factory.Create(parameter, parameter.ParameterPolicies[0]);
// Assert
Assert.IsType<CustomParameterPolicy>(parameterPolicy);
}
private class CustomParameterPolicy : IParameterPolicy
{
}
[Fact]
public void Create_CreatesParameterPolicy_FromConstraintText_AndRouteConstraint()
{
// Arrange
var factory = GetParameterPolicyFactory();
// Act
var parameterPolicy = factory.Create(RoutePatternFactory.ParameterPart("id"), "int");
// Assert
Assert.IsType<IntRouteConstraint>(parameterPolicy);
}
[Fact]
public void Create_CreatesParameterPolicy_FromConstraintText_AndRouteConstraint_Optional()
{
// Arrange
var factory = GetParameterPolicyFactory();
// Act
var parameterPolicy = factory.Create(RoutePatternFactory.ParameterPart("id", @default: null, RoutePatternParameterKind.Optional), "int");
// Assert
var optionalConstraint = Assert.IsType<OptionalRouteConstraint>(parameterPolicy);
Assert.IsType<IntRouteConstraint>(optionalConstraint.InnerConstraint);
}
[Fact]
public void Create_CreatesParameterPolicy_FromConstraintText_AndParameterPolicy()
{
// Arrange
var options = new RouteOptions();
options.ConstraintMap.Add("customParameterPolicy", typeof(CustomParameterPolicy));
var services = new ServiceCollection();
services.AddTransient<CustomParameterPolicy>();
var factory = GetParameterPolicyFactory(options, services);
// Act
var parameterPolicy = factory.Create(RoutePatternFactory.ParameterPart("id", @default: null, RoutePatternParameterKind.Optional), "customParameterPolicy");
// Assert
Assert.IsType<CustomParameterPolicy>(parameterPolicy);
}
[Fact]
public void Create_CreatesParameterPolicy_FromConstraintText_AndParameterPolicy_Optional()
{
// Arrange
var options = new RouteOptions();
options.ConstraintMap.Add("customParameterPolicy", typeof(CustomParameterPolicy));
var services = new ServiceCollection();
services.AddTransient<CustomParameterPolicy>();
var factory = GetParameterPolicyFactory(options, services);
// Act
var parameterPolicy = factory.Create(RoutePatternFactory.ParameterPart("id", @default: null, RoutePatternParameterKind.Optional), "customParameterPolicy");
// Assert
Assert.IsType<CustomParameterPolicy>(parameterPolicy);
}
private DefaultParameterPolicyFactory GetParameterPolicyFactory(
RouteOptions options = null,
ServiceCollection services = null)
{
if (options == null)
{
options = new RouteOptions();
}
if (services == null)
{
services = new ServiceCollection();
}
return new DefaultParameterPolicyFactory(
Options.Create(options),
services.BuildServiceProvider());
}
private class TestRouteConstraint : IRouteConstraint
{
private TestRouteConstraint() { }
public HttpContext HttpContext { get; private set; }
public IRouter Route { get; private set; }
public string RouteKey { get; private set; }
public RouteValueDictionary Values { get; private set; }
public RouteDirection RouteDirection { get; private set; }
public static TestRouteConstraint Create()
{
return new TestRouteConstraint();
}
public bool Match(
HttpContext httpContext,
IRouter route,
string routeKey,
RouteValueDictionary values,
RouteDirection routeDirection)
{
HttpContext = httpContext;
Route = route;
RouteKey = routeKey;
Values = values;
RouteDirection = routeDirection;
return false;
}
}
}
}

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Routing.Constraints;
using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;
@ -641,7 +642,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
Assert.Empty(candidate.Captures);
Assert.Equal(default, candidate.CatchAll);
Assert.Empty(candidate.ComplexSegments);
Assert.Empty(candidate.MatchProcessors);
Assert.Empty(candidate.Constraints);
}
[Fact]
@ -665,7 +666,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
c => Assert.Equal(("c", 2, 2), c));
Assert.Equal(default, candidate.CatchAll);
Assert.Empty(candidate.ComplexSegments);
Assert.Empty(candidate.MatchProcessors);
Assert.Empty(candidate.Constraints);
}
[Fact]
@ -695,7 +696,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
c => Assert.Equal(("c", 2, 2), c));
Assert.Equal(default, candidate.CatchAll);
Assert.Empty(candidate.ComplexSegments);
Assert.Empty(candidate.MatchProcessors);
Assert.Empty(candidate.Constraints);
}
[Fact]
@ -726,7 +727,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
c => Assert.Equal(("b", 1, 2), c));
Assert.Equal(("c", 2, 0), candidate.CatchAll);
Assert.Empty(candidate.ComplexSegments);
Assert.Empty(candidate.MatchProcessors);
Assert.Empty(candidate.Constraints);
}
// Defaults are processed first, which affects the slot ordering.
@ -758,7 +759,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
c => Assert.Equal(("c", 2, 2), c));
Assert.Equal(default, candidate.CatchAll);
Assert.Empty(candidate.ComplexSegments);
Assert.Empty(candidate.MatchProcessors);
Assert.Empty(candidate.Constraints);
}
[Fact]
@ -789,11 +790,11 @@ namespace Microsoft.AspNetCore.Routing.Matching
Assert.Collection(
candidate.ComplexSegments,
s => Assert.Equal(0, s.segmentIndex));
Assert.Empty(candidate.MatchProcessors);
Assert.Empty(candidate.Constraints);
}
[Fact]
public void CreateCandidate_MatchProcessors()
public void CreateCandidate_RouteConstraints()
{
// Arrange
var endpoint = CreateEndpoint("/a/b/c", constraints: new { a = new IntRouteConstraint(), });
@ -804,14 +805,38 @@ namespace Microsoft.AspNetCore.Routing.Matching
var candidate = builder.CreateCandidate(endpoint, score: 0);
// Assert
Assert.Equal( Candidate.CandidateFlags.HasMatchProcessors, candidate.Flags);
Assert.Equal(Candidate.CandidateFlags.HasConstraints, candidate.Flags);
Assert.Empty(candidate.Slots);
Assert.Empty(candidate.Captures);
Assert.Equal(default, candidate.CatchAll);
Assert.Empty(candidate.ComplexSegments);
Assert.Single(candidate.MatchProcessors);
Assert.Single(candidate.Constraints);
}
[Fact]
public void CreateCandidate_CustomParameterPolicy()
{
// Arrange
var endpoint = CreateEndpoint("/a/b/c", constraints: new { a = new CustomParameterPolicy(), });
var builder = CreateDfaMatcherBuilder();
// Act
var candidate = builder.CreateCandidate(endpoint, score: 0);
// Assert
Assert.Equal(Candidate.CandidateFlags.None, candidate.Flags);
Assert.Empty(candidate.Slots);
Assert.Empty(candidate.Captures);
Assert.Equal(default, candidate.CatchAll);
Assert.Empty(candidate.ComplexSegments);
Assert.Empty(candidate.Constraints);
}
private class CustomParameterPolicy : IParameterPolicy
{
}
[Fact]
public void CreateCandidates_CreatesScoresCorrectly()
{
@ -846,7 +871,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
{
var dataSource = new CompositeEndpointDataSource(Array.Empty<EndpointDataSource>());
return new DfaMatcherBuilder(
Mock.Of<MatchProcessorFactory>(),
new DefaultParameterPolicyFactory(Options.Create(new RouteOptions()), Mock.Of<IServiceProvider>()),
Mock.Of<EndpointSelector>(),
policies);
}

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
{
return new MatcherEndpoint(
MatcherEndpoint.EmptyInvoker,
RoutePatternFactory.Parse(template, defaults, constraints: null),
RoutePatternFactory.Parse(template, defaults, parameterPolicies: null),
order,
metadata ?? EndpointMetadataCollection.Empty,
template);

View File

@ -1,62 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Http;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Routing.Matching
{
public class RouteConstraintMatchProcessorTest
{
[Fact]
public void MatchInbound_CallsRouteConstraint()
{
// Arrange
var constraint = new Mock<IRouteConstraint>();
constraint
.Setup(c => c.Match(
It.IsAny<HttpContext>(),
NullRouter.Instance,
"test",
It.IsAny<RouteValueDictionary>(),
RouteDirection.IncomingRequest))
.Returns(true)
.Verifiable();
var matchProcessor = new RouteConstraintMatchProcessor("test", constraint.Object);
// Act
var result = matchProcessor.ProcessInbound(new DefaultHttpContext(), new RouteValueDictionary());
// Assert
Assert.True(result);
constraint.Verify();
}
[Fact]
public void MatchOutput_CallsRouteConstraint()
{
// Arrange
var constraint = new Mock<IRouteConstraint>();
constraint
.Setup(c => c.Match(
It.IsAny<HttpContext>(),
NullRouter.Instance,
"test",
It.IsAny<RouteValueDictionary>(),
RouteDirection.UrlGeneration))
.Returns(true)
.Verifiable();
var matchProcessor = new RouteConstraintMatchProcessor("test", constraint.Object);
// Act
var result = matchProcessor.ProcessOutbound(new DefaultHttpContext(), new RouteValueDictionary());
// Assert
Assert.True(result);
constraint.Verify();
}
}
}

View File

@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Equal(parameterName, templatePart.Name);
Assert.Null(templatePart.Default);
Assert.Empty(templatePart.Constraints);
Assert.Empty(templatePart.ParameterPolicies);
}
[Fact]
@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Equal("param", templatePart.Name);
Assert.Equal("", templatePart.Default);
Assert.Empty(templatePart.Constraints);
Assert.Empty(templatePart.ParameterPolicies);
}
[Fact]
@ -44,7 +44,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Equal("param", templatePart.Name);
Assert.Null(templatePart.Default);
Assert.Empty(templatePart.Constraints);
Assert.Empty(templatePart.ParameterPolicies);
}
[Fact]
@ -56,7 +56,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Equal("param", templatePart.Name);
Assert.Equal("", templatePart.Default);
Assert.Empty(templatePart.Constraints);
Assert.Empty(templatePart.ParameterPolicies);
}
[Fact]
@ -68,7 +68,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Equal("param", templatePart.Name);
Assert.Equal(":", templatePart.Default);
Assert.Empty(templatePart.Constraints);
Assert.Empty(templatePart.ParameterPolicies);
}
[Fact]
@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("param", templatePart.Name);
Assert.Equal("111111", templatePart.Default);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal("int", constraint.Content);
}
@ -95,7 +95,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("param", templatePart.Name);
Assert.Equal("111111", templatePart.Default);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(\d+)", constraint.Content);
}
@ -109,7 +109,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("param", templatePart.Name);
Assert.True(templatePart.IsOptional);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal("int", constraint.Content);
}
@ -124,7 +124,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("12", templatePart.Default);
Assert.True(templatePart.IsOptional);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal("int", constraint.Content);
}
@ -139,7 +139,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("12?", templatePart.Default);
Assert.True(templatePart.IsOptional);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal("int", constraint.Content);
}
@ -153,7 +153,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("param", templatePart.Name);
Assert.True(templatePart.IsOptional);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(\d+)", constraint.Content);
}
@ -169,7 +169,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("abc", templatePart.Default);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(\d+)", constraint.Content);
}
@ -182,7 +182,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Equal("param", templatePart.Name);
Assert.Collection(templatePart.Constraints,
Assert.Collection(templatePart.ParameterPolicies,
constraint => Assert.Equal(@"test(d+)", constraint.Content),
constraint => Assert.Equal(@"test(w+)", constraint.Content));
}
@ -197,7 +197,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("param", templatePart.Name);
Assert.Collection(
templatePart.Constraints,
templatePart.ParameterPolicies,
constraint => Assert.Equal(@"test(d+)", constraint.Content),
constraint => Assert.Equal(@"test(w+)", constraint.Content));
}
@ -211,7 +211,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Equal("param", templatePart.Name);
Assert.Collection(templatePart.Constraints,
Assert.Collection(templatePart.ParameterPolicies,
constraint => Assert.Equal(@"test(\d+)", constraint.Content),
constraint => Assert.Equal(@"test(\w:+)", constraint.Content));
}
@ -227,7 +227,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("qwer", templatePart.Default);
Assert.Collection(templatePart.Constraints,
Assert.Collection(templatePart.ParameterPolicies,
constraint => Assert.Equal(@"test(\d+)", constraint.Content),
constraint => Assert.Equal(@"test(\w+)", constraint.Content));
}
@ -244,7 +244,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("=qwer", templatePart.Default);
Assert.Collection(
templatePart.Constraints,
templatePart.ParameterPolicies,
constraint => Assert.Equal(@"test(\d+)", constraint.Content),
constraint => Assert.Equal(@"test(\w+)", constraint.Content));
}
@ -262,7 +262,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("comparison-operator", templatePart.Name);
Assert.Equal(defaultValue, templatePart.Default);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal("length(6)", constraint.Content);
}
@ -280,7 +280,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("hello", param1.Default);
Assert.False(param1.IsOptional);
Assert.Collection(param1.Constraints,
Assert.Collection(param1.ParameterPolicies,
constraint => Assert.Equal("int", constraint.Content),
constraint => Assert.Equal("test(3)", constraint.Content)
);
@ -325,7 +325,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Equal("param", templatePart.Name);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(\})", constraint.Content);
}
@ -340,7 +340,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("wer", templatePart.Default);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(\})", constraint.Content);
}
@ -353,7 +353,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Equal("param", templatePart.Name);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(\))", constraint.Content);
}
@ -368,7 +368,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("fsd", templatePart.Default);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(\))", constraint.Content);
}
@ -381,7 +381,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Equal("param", templatePart.Name);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(:)", constraint.Content);
}
@ -396,7 +396,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("mnf", templatePart.Default);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(:)", constraint.Content);
}
@ -409,7 +409,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Equal("param", templatePart.Name);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(a:b:c)", constraint.Content);
}
@ -424,7 +424,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("12", templatePart.Default);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal("test", constraint.Content);
}
@ -440,7 +440,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("12", templatePart.Default);
Assert.Collection(
templatePart.Constraints,
templatePart.ParameterPolicies,
constraint => Assert.Equal("test", constraint.Content));
}
@ -454,7 +454,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal(":param", templatePart.Name);
Assert.Collection(
templatePart.Constraints,
templatePart.ParameterPolicies,
constraint => Assert.Equal("test", constraint.Content));
}
@ -467,7 +467,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Equal("param", templatePart.Name);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(\w,\w)", constraint.Content);
}
@ -480,7 +480,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Equal("par,am", templatePart.Name);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(\w)", constraint.Content);
}
@ -495,7 +495,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("jsd", templatePart.Default);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(\w,\w)", constraint.Content);
}
@ -511,7 +511,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.True(templatePart.IsOptional);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal("int", constraint.Content);
}
@ -525,7 +525,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("param", templatePart.Name);
Assert.Null(templatePart.Default);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal("test(=)", constraint.Content);
}
@ -550,7 +550,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("param", templatePart.Name);
Assert.Null(templatePart.Default);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal("test(a==b)", constraint.Content);
}
@ -564,7 +564,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("param", templatePart.Name);
Assert.Equal("dvds", templatePart.Default);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal("test(a==b)", constraint.Content);
}
@ -622,7 +622,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("param", templatePart.Name);
Assert.Equal("sds", templatePart.Default);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal("test(=)", constraint.Content);
}
@ -635,7 +635,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Equal("param", templatePart.Name);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(\{)", constraint.Content);
}
@ -648,7 +648,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Equal("par{am", templatePart.Name);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(\sd)", constraint.Content);
}
@ -663,7 +663,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("xvc", templatePart.Default);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(\{)", constraint.Content);
}
@ -676,7 +676,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Equal("par(am", templatePart.Name);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(\()", constraint.Content);
}
@ -689,7 +689,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Equal("param", templatePart.Name);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(\()", constraint.Content);
}
@ -702,7 +702,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Equal("param", templatePart.Name);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal("test(#$%", constraint.Content);
}
@ -715,7 +715,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Equal("param", templatePart.Name);
Assert.Collection(templatePart.Constraints,
Assert.Collection(templatePart.ParameterPolicies,
constraint => Assert.Equal(@"test(#", constraint.Content),
constraint => Assert.Equal(@"test1", constraint.Content));
}
@ -730,7 +730,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("param", templatePart.Name);
Assert.Equal("default-value", templatePart.Default);
Assert.Collection(templatePart.Constraints,
Assert.Collection(templatePart.ParameterPolicies,
constraint => Assert.Equal(@"test(abc:somevalue)", constraint.Content),
constraint => Assert.Equal(@"name(test1", constraint.Content),
constraint => Assert.Equal(@"differentname", constraint.Content));
@ -746,7 +746,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("param", templatePart.Name);
Assert.Equal("test1", templatePart.Default);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(constraintvalue", constraint.Content);
}
@ -761,7 +761,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("djk", templatePart.Default);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(\()", constraint.Content);
}
@ -776,7 +776,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Null(templatePart.Default);
Assert.False(templatePart.IsOptional);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(\?)", constraint.Content);
}
@ -791,7 +791,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Null(templatePart.Default);
Assert.True(templatePart.IsOptional);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(\?)", constraint.Content);
}
@ -806,7 +806,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("sdf", templatePart.Default);
Assert.False(templatePart.IsOptional);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(\?)", constraint.Content);
}
@ -821,7 +821,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("sdf", templatePart.Default);
Assert.True(templatePart.IsOptional);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(\?)", constraint.Content);
}
@ -836,7 +836,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Null(templatePart.Default);
Assert.False(templatePart.IsOptional);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(\?)", constraint.Content);
}
@ -851,7 +851,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Null(templatePart.Default);
Assert.False(templatePart.IsOptional);
Assert.Collection(templatePart.Constraints,
Assert.Collection(templatePart.ParameterPolicies,
constraint => Assert.Equal(@"test(#)", constraint.Content),
constraint => Assert.Equal(@"$)", constraint.Content));
}
@ -867,7 +867,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Null(templatePart.Default);
Assert.False(templatePart.IsOptional);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"test(#:)$)", constraint.Content);
}
@ -882,7 +882,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Null(templatePart.Default);
Assert.False(templatePart.IsOptional);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"regex(\\(\\(\\(\\()", constraint.Content);
}
@ -897,7 +897,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Null(templatePart.Default);
Assert.False(templatePart.IsOptional);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"regex(^\d{{3}}-\d{{3}}-\d{{4}}$)", constraint.Content);
}
@ -912,7 +912,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("123-456-7890", templatePart.Default);
Assert.False(templatePart.IsOptional);
var constraint = Assert.Single(templatePart.Constraints);
var constraint = Assert.Single(templatePart.ParameterPolicies);
Assert.Equal(@"regex(^\d{{3}}-\d{{3}}-\d{{4}}$)", constraint.Content);
}
@ -934,7 +934,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Equal(expectedParameterName, templatePart.Name);
Assert.Empty(templatePart.Constraints);
Assert.Empty(templatePart.ParameterPolicies);
Assert.Null(templatePart.Default);
}
@ -979,7 +979,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("path", parameterPart.Name);
Assert.True(parameterPart.IsCatchAll);
Assert.Equal(RoutePatternParameterKind.CatchAll, parameterPart.ParameterKind);
var constraintReference = Assert.Single(parameterPart.Constraints);
var constraintReference = Assert.Single(parameterPart.ParameterPolicies);
Assert.Equal(constraintContent, constraintReference.Content);
Assert.True(parameterPart.EncodeSlashes);
}
@ -997,7 +997,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("path", parameterPart.Name);
Assert.True(parameterPart.IsCatchAll);
Assert.Equal(RoutePatternParameterKind.CatchAll, parameterPart.ParameterKind);
var constraintReference = Assert.Single(parameterPart.Constraints);
var constraintReference = Assert.Single(parameterPart.ParameterPolicies);
Assert.Equal(constraintContent, constraintReference.Content);
Assert.NotNull(parameterPart.Default);
Assert.Equal("a/b/c", parameterPart.Default.ToString());
@ -1043,7 +1043,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("path", parameterPart.Name);
Assert.True(parameterPart.IsCatchAll);
Assert.False(parameterPart.EncodeSlashes);
var constraintReference = Assert.Single(parameterPart.Constraints);
var constraintReference = Assert.Single(parameterPart.ParameterPolicies);
Assert.Equal(constraintContent, constraintReference.Content);
}
@ -1060,7 +1060,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("path", parameterPart.Name);
Assert.True(parameterPart.IsCatchAll);
Assert.False(parameterPart.EncodeSlashes);
var constraintReference = Assert.Single(parameterPart.Constraints);
var constraintReference = Assert.Single(parameterPart.ParameterPolicies);
Assert.Equal(constraintContent, constraintReference.Content);
Assert.NotNull(parameterPart.Default);
Assert.Equal("a/b/c", parameterPart.Default.ToString());

View File

@ -132,21 +132,21 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Collection(
actual.GetParameter("a").Constraints,
c => Assert.IsType<RegexRouteConstraint>(c.Constraint),
actual.GetParameter("a").ParameterPolicies,
c => Assert.IsType<RegexRouteConstraint>(c.ParameterPolicy),
c => Assert.Equal("int", c.Content));
Assert.Collection(
actual.GetParameter("b").Constraints,
c => Assert.IsType<RegexRouteConstraint>(c.Constraint));
actual.GetParameter("b").ParameterPolicies,
c => Assert.IsType<RegexRouteConstraint>(c.ParameterPolicy));
Assert.Collection(
actual.Constraints.OrderBy(kvp => kvp.Key),
actual.ParameterPolicies.OrderBy(kvp => kvp.Key),
kvp =>
{
Assert.Equal("a", kvp.Key);
Assert.Collection(
kvp.Value,
c => Assert.IsType<RegexRouteConstraint>(c.Constraint),
c => Assert.IsType<RegexRouteConstraint>(c.ParameterPolicy),
c => Assert.Equal("int", c.Content));
},
kvp =>
@ -154,7 +154,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
Assert.Equal("b", kvp.Key);
Assert.Collection(
kvp.Value,
c => Assert.IsType<RegexRouteConstraint>(c.Constraint));
c => Assert.IsType<RegexRouteConstraint>(c.ParameterPolicy));
});
}
@ -177,30 +177,30 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Collection(
actual.Constraints.OrderBy(kvp => kvp.Key),
actual.ParameterPolicies.OrderBy(kvp => kvp.Key),
kvp =>
{
Assert.Equal("d", kvp.Key);
Assert.Collection(
kvp.Value,
c => Assert.IsType<RegexRouteConstraint>(c.Constraint));
c => Assert.IsType<RegexRouteConstraint>(c.ParameterPolicy));
},
kvp =>
{
Assert.Equal("e", kvp.Key);
Assert.Collection(
kvp.Value,
c => Assert.IsType<RegexRouteConstraint>(c.Constraint));
c => Assert.IsType<RegexRouteConstraint>(c.ParameterPolicy));
});
}
[Fact]
public void Pattern_ExtraConstraints_MatchProcessor()
public void Pattern_ExtraConstraints_RouteConstraint()
{
// Arrange
var template = "{a}/{b}/{c}";
var defaults = new { };
var constraints = new { d = Mock.Of<MatchProcessor>(), e = Mock.Of<MatchProcessor>(), };
var constraints = new { d = Mock.Of<IRouteConstraint>(), e = Mock.Of<IRouteConstraint>(), };
var original = RoutePatternFactory.Parse(template);
@ -213,20 +213,20 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Collection(
actual.Constraints.OrderBy(kvp => kvp.Key),
actual.ParameterPolicies.OrderBy(kvp => kvp.Key),
kvp =>
{
Assert.Equal("d", kvp.Key);
Assert.Collection(
kvp.Value,
c => Assert.NotNull(c.MatchProcessor));
c => Assert.NotNull(c.ParameterPolicy));
},
kvp =>
{
Assert.Equal("e", kvp.Key);
Assert.Collection(
kvp.Value,
c => Assert.NotNull(c.MatchProcessor));
c => Assert.NotNull(c.ParameterPolicy));
});
}
@ -249,11 +249,11 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Collection(
actual.Constraints.OrderBy(kvp => kvp.Key),
actual.ParameterPolicies.OrderBy(kvp => kvp.Key),
kvp =>
{
Assert.Equal("d", kvp.Key);
var regex = Assert.IsType<RegexRouteConstraint>(Assert.Single(kvp.Value).Constraint);
var regex = Assert.IsType<RegexRouteConstraint>(Assert.Single(kvp.Value).ParameterPolicy);
Assert.Equal("^(foo)$", regex.Constraint.ToString());
});
}
@ -277,7 +277,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
// Assert
Assert.Equal(
$"Invalid constraint '17'. A constraint must be of type 'string', '{typeof(IRouteConstraint)}', or '{typeof(MatchProcessor)}'.",
$"Invalid constraint '17'. A constraint must be of type 'string' or '{typeof(IRouteConstraint)}'.",
ex.Message);
}
}

View File

@ -641,7 +641,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
private class RoutePatternEqualityComparer :
IEqualityComparer<RoutePattern>,
IEqualityComparer<RoutePatternConstraintReference>
IEqualityComparer<RoutePatternParameterPolicyReference>
{
public bool Equals(RoutePattern x, RoutePattern y)
{
@ -733,15 +733,15 @@ namespace Microsoft.AspNetCore.Routing.Patterns
x.Name == y.Name &&
x.Default == y.Default &&
x.ParameterKind == y.ParameterKind &&
Enumerable.SequenceEqual(x.Constraints, y.Constraints, this);
Enumerable.SequenceEqual(x.ParameterPolicies, y.ParameterPolicies, this);
}
public bool Equals(RoutePatternConstraintReference x, RoutePatternConstraintReference y)
public bool Equals(RoutePatternParameterPolicyReference x, RoutePatternParameterPolicyReference y)
{
return
x.Content == y.Content &&
x.Constraint == y.Constraint;
x.ParameterPolicy == y.ParameterPolicy;
}
private bool Equals(RoutePatternSeparatorPart x, RoutePatternSeparatorPart y)
@ -754,7 +754,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
throw new NotImplementedException();
}
public int GetHashCode(RoutePatternConstraintReference obj)
public int GetHashCode(RoutePatternParameterPolicyReference obj)
{
throw new NotImplementedException();
}

View File

@ -251,7 +251,7 @@ namespace Microsoft.AspNetCore.Routing
return new MatcherEndpoint(
MatcherEndpoint.EmptyInvoker,
RoutePatternFactory.Parse(template, defaults, constraints: null),
RoutePatternFactory.Parse(template, defaults, parameterPolicies: null),
order,
metadataCollection,
null);