From f3836ae4b6d4ea9895745ac18af19331b4b58af5 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 22 Feb 2023 17:03:20 +0800 Subject: [PATCH] Refactor RoutePatternFactory to trim regex dependency where possible (#46793) --- .../src/Patterns/RoutePatternFactory.cs | 121 ++++++++++-------- 1 file changed, 65 insertions(+), 56 deletions(-) diff --git a/src/Http/Routing/src/Patterns/RoutePatternFactory.cs b/src/Http/Routing/src/Patterns/RoutePatternFactory.cs index 0a87d2a7b434..f6e1760a42ed 100644 --- a/src/Http/Routing/src/Patterns/RoutePatternFactory.cs +++ b/src/Http/Routing/src/Patterns/RoutePatternFactory.cs @@ -58,7 +58,7 @@ public static RoutePattern Parse([StringSyntax("Route")] string pattern, object? ArgumentNullException.ThrowIfNull(pattern); var original = RoutePatternParser.Parse(pattern); - return PatternCore(original.RawText, Wrap(defaults), Wrap(parameterPolicies), requiredValues: null, original.PathSegments); + return PatternCore(original.RawText, Wrap(defaults), CreateRoutePatternPolicyReferences(Wrap(parameterPolicies)), requiredValues: null, original.PathSegments); } /// @@ -83,7 +83,7 @@ public static RoutePattern Parse([StringSyntax("Route")] string pattern, RouteVa ArgumentNullException.ThrowIfNull(pattern); var original = RoutePatternParser.Parse(pattern); - return PatternCore(original.RawText, defaults, parameterPolicies, requiredValues: null, original.PathSegments); + return PatternCore(original.RawText, defaults, CreateRoutePatternPolicyReferences(parameterPolicies), requiredValues: null, original.PathSegments); } /// @@ -112,7 +112,7 @@ public static RoutePattern Parse([StringSyntax("Route")] string pattern, object? ArgumentNullException.ThrowIfNull(pattern); var original = RoutePatternParser.Parse(pattern); - return PatternCore(original.RawText, Wrap(defaults), Wrap(parameterPolicies), Wrap(requiredValues), original.PathSegments); + return PatternCore(original.RawText, Wrap(defaults), CreateRoutePatternPolicyReferences(Wrap(parameterPolicies)), Wrap(requiredValues), original.PathSegments); } /// @@ -140,7 +140,7 @@ public static RoutePattern Parse([StringSyntax("Route")] string pattern, RouteVa ArgumentNullException.ThrowIfNull(pattern); var original = RoutePatternParser.Parse(pattern); - return PatternCore(original.RawText, defaults, parameterPolicies, requiredValues, original.PathSegments); + return PatternCore(original.RawText, defaults, CreateRoutePatternPolicyReferences(parameterPolicies), requiredValues, original.PathSegments); } /// @@ -193,7 +193,7 @@ public static RoutePattern Pattern( { ArgumentNullException.ThrowIfNull(segments); - return PatternCore(null, new RouteValueDictionary(defaults), new RouteValueDictionary(parameterPolicies), requiredValues: null, segments); + return PatternCore(null, Wrap(defaults), CreateRoutePatternPolicyReferences(Wrap(parameterPolicies)), requiredValues: null, segments); } /// @@ -220,7 +220,7 @@ public static RoutePattern Pattern( { ArgumentNullException.ThrowIfNull(segments); - return PatternCore(null, defaults, parameterPolicies, requiredValues: null, segments); + return PatternCore(null, defaults, CreateRoutePatternPolicyReferences(parameterPolicies), requiredValues: null, segments); } /// @@ -250,7 +250,7 @@ public static RoutePattern Pattern( { ArgumentNullException.ThrowIfNull(segments); - return PatternCore(rawText, new RouteValueDictionary(defaults), new RouteValueDictionary(parameterPolicies), requiredValues: null, segments); + return PatternCore(rawText, Wrap(defaults), CreateRoutePatternPolicyReferences(Wrap(parameterPolicies)), requiredValues: null, segments); } /// @@ -279,7 +279,7 @@ public static RoutePattern Pattern( { ArgumentNullException.ThrowIfNull(segments); - return PatternCore(rawText, defaults, parameterPolicies, requiredValues: null, segments); + return PatternCore(rawText, defaults, CreateRoutePatternPolicyReferences(parameterPolicies), requiredValues: null, segments); } /// @@ -332,7 +332,7 @@ public static RoutePattern Pattern( { ArgumentNullException.ThrowIfNull(segments); - return PatternCore(null, new RouteValueDictionary(defaults), new RouteValueDictionary(parameterPolicies), requiredValues: null, segments); + return PatternCore(null, Wrap(defaults), CreateRoutePatternPolicyReferences(Wrap(parameterPolicies)), requiredValues: null, segments); } /// @@ -359,7 +359,7 @@ public static RoutePattern Pattern( { ArgumentNullException.ThrowIfNull(segments); - return PatternCore(null, defaults, parameterPolicies, requiredValues: null, segments); + return PatternCore(null, defaults, CreateRoutePatternPolicyReferences(parameterPolicies), requiredValues: null, segments); } /// @@ -389,7 +389,7 @@ public static RoutePattern Pattern( { ArgumentNullException.ThrowIfNull(segments); - return PatternCore(rawText, new RouteValueDictionary(defaults), new RouteValueDictionary(parameterPolicies), requiredValues: null, segments); + return PatternCore(rawText, Wrap(defaults), CreateRoutePatternPolicyReferences(Wrap(parameterPolicies)), requiredValues: null, segments); } /// @@ -418,13 +418,13 @@ public static RoutePattern Pattern( { ArgumentNullException.ThrowIfNull(segments); - return PatternCore(rawText, defaults, parameterPolicies, requiredValues: null, segments); + return PatternCore(rawText, defaults, CreateRoutePatternPolicyReferences(parameterPolicies), requiredValues: null, segments); } private static RoutePattern PatternCore( string? rawText, RouteValueDictionary? defaults, - RouteValueDictionary? parameterPolicies, + Dictionary>? parameterPolicyReferences, RouteValueDictionary? requiredValues, IEnumerable segments) { @@ -450,43 +450,6 @@ private static RoutePattern PatternCore( } } - Dictionary>? updatedParameterPolicies = null; - if (parameterPolicies != null && parameterPolicies.Count > 0) - { - updatedParameterPolicies = new Dictionary>(parameterPolicies.Count, StringComparer.OrdinalIgnoreCase); - - foreach (var kvp in parameterPolicies) - { - var policyReferences = new List(); - - if (kvp.Value is IParameterPolicy parameterPolicy) - { - policyReferences.Add(ParameterPolicy(parameterPolicy)); - } - else if (kvp.Value is string) - { - // Constraint will convert string values into regex constraints - policyReferences.Add(Constraint(kvp.Value)); - } - else if (kvp.Value is IEnumerable multiplePolicies) - { - foreach (var item in multiplePolicies) - { - // Constraint will convert string values into regex constraints - policyReferences.Add(item is IParameterPolicy p ? ParameterPolicy(p) : Constraint(item)); - } - } - else - { - throw new InvalidOperationException(Resources.FormatRoutePattern_InvalidConstraintReference( - kvp.Value ?? "null", - typeof(IRouteConstraint))); - } - - updatedParameterPolicies.Add(kvp.Key, policyReferences); - } - } - List? parameters = null; var updatedSegments = segments.ToArray(); for (var i = 0; i < updatedSegments.Length; i++) @@ -554,8 +517,8 @@ private static RoutePattern PatternCore( return new RoutePattern( rawText, updatedDefaults ?? EmptyDictionary, - updatedParameterPolicies != null - ? updatedParameterPolicies.ToDictionary(kvp => kvp.Key, kvp => (IReadOnlyList)kvp.Value.ToArray()) + parameterPolicyReferences != null + ? parameterPolicyReferences.ToDictionary(kvp => kvp.Key, kvp => (IReadOnlyList)kvp.Value.ToArray()) : EmptyPoliciesDictionary, requiredValues ?? EmptyDictionary, (IReadOnlyList?)parameters ?? Array.Empty(), @@ -627,16 +590,16 @@ RoutePatternPart VisitPart(RoutePatternPart part) } List? parameterConstraints = null; - if ((updatedParameterPolicies == null || !updatedParameterPolicies.TryGetValue(parameter.Name, out parameterConstraints)) && + if ((parameterPolicyReferences == null || !parameterPolicyReferences.TryGetValue(parameter.Name, out parameterConstraints)) && parameter.ParameterPolicies.Count > 0) { - if (updatedParameterPolicies == null) + if (parameterPolicyReferences == null) { - updatedParameterPolicies = new Dictionary>(StringComparer.OrdinalIgnoreCase); + parameterPolicyReferences = new Dictionary>(StringComparer.OrdinalIgnoreCase); } parameterConstraints = new List(parameter.ParameterPolicies.Count); - updatedParameterPolicies.Add(parameter.Name, parameterConstraints); + parameterPolicyReferences.Add(parameter.Name, parameterConstraints); } if (parameter.ParameterPolicies.Count > 0) @@ -661,6 +624,52 @@ RoutePatternPart VisitPart(RoutePatternPart part) } } + /// + /// String policy references are infered to be regex constraints. Creating them is moved here to its own method so apps can + /// trim away the regex dependency when RoutePatternFactory.Parse(string) is used. This is the method typically used by the various Map methods. + /// + private static Dictionary>? CreateRoutePatternPolicyReferences(RouteValueDictionary? parameterPolicies) + { + Dictionary>? updatedParameterPolicies = null; + if (parameterPolicies != null && parameterPolicies.Count > 0) + { + updatedParameterPolicies = new Dictionary>(parameterPolicies.Count, StringComparer.OrdinalIgnoreCase); + + foreach (var kvp in parameterPolicies) + { + var policyReferences = new List(); + + if (kvp.Value is IParameterPolicy parameterPolicy) + { + policyReferences.Add(ParameterPolicy(parameterPolicy)); + } + else if (kvp.Value is string) + { + // Constraint will convert string values into regex constraints + policyReferences.Add(Constraint(kvp.Value)); + } + else if (kvp.Value is IEnumerable multiplePolicies) + { + foreach (var item in multiplePolicies) + { + // Constraint will convert string values into regex constraints + policyReferences.Add(item is IParameterPolicy p ? ParameterPolicy(p) : Constraint(item)); + } + } + else + { + throw new InvalidOperationException(Resources.FormatRoutePattern_InvalidConstraintReference( + kvp.Value ?? "null", + typeof(IRouteConstraint))); + } + + updatedParameterPolicies.Add(kvp.Key, policyReferences); + } + } + + return updatedParameterPolicies; + } + /// /// Creates a from the provided collection /// of parts.