From 763720b092b124b4cb179296e450ff23b2ce097a Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Thu, 11 Apr 2019 16:01:39 -0700 Subject: [PATCH] Remove Routing pubternal This change makes a bunch of random routing classes internal instead of pubternal. Most of these have no use case and aren't valuable at all outside of routing code. The one exception here is that the only way to construct a `TemplateBinder` in 1.0-2.2 is using a constructor that accepts some pubternal types. I don't think it's a good idea to just yank this since the usage is pretty wide. Instead I added a factory service so you can create this, and marked the constructor [Obsolete] where these types are touched. We can make the actual breaking change in the next major release. --- ...rosoft.AspNetCore.Routing.netcoreapp3.0.cs | 66 +++---------- .../src/{Internal => }/ArrayBuilder.cs | 6 +- src/Http/Routing/src/DefaultLinkGenerator.cs | 41 +------- .../RoutingServiceCollectionExtensions.cs | 4 + src/Http/Routing/src/Internal/BufferValue.cs | 18 ---- src/Http/Routing/src/Internal/SegmentState.cs | 3 + .../src/Internal/UriBuildingContext.cs | 15 +++ .../Routing/src/{Internal => }/NullRouter.cs | 0 .../ParameterPolicyActivator.cs | 4 +- .../src/{Internal => }/PathTokenizer.cs | 4 +- src/Http/Routing/src/RouteBase.cs | 6 +- .../{Internal => }/RoutingMarkerService.cs | 4 +- .../Template/DefaultTemplateBinderFactory.cs | 95 +++++++++++++++++++ .../Routing/src/Template/TemplateBinder.cs | 62 ++++++++++++ .../src/Template/TemplateBinderFactory.cs | 29 ++++++ .../LinkGenerationDecisionTree.cs | 5 +- .../{Internal => Tree}/OutboundMatchResult.cs | 6 +- src/Http/Routing/src/Tree/TreeRouteBuilder.cs | 8 +- src/Http/Routing/src/Tree/TreeRouter.cs | 3 + .../UriBuilderContextPooledObjectPolicy.cs | 7 +- .../Internal/UriBuildingContextTest.cs | 6 +- .../test/UnitTests/LinkGeneratorTestBase.cs | 3 +- .../Matching/TreeRouterMatcherBuilder.cs | 4 +- src/Http/Routing/test/UnitTests/RouteTest.cs | 2 + .../UnitTests/Template/TemplateBinderTests.cs | 2 + .../UnitTests/Tree/TreeRouteBuilderTest.cs | 4 +- .../test/UnitTests/Tree/TreeRouterTest.cs | 4 + 27 files changed, 276 insertions(+), 135 deletions(-) rename src/Http/Routing/src/{Internal => }/ArrayBuilder.cs (97%) delete mode 100644 src/Http/Routing/src/Internal/BufferValue.cs rename src/Http/Routing/src/{Internal => }/NullRouter.cs (100%) rename src/Http/Routing/src/{Internal => }/ParameterPolicyActivator.cs (98%) rename src/Http/Routing/src/{Internal => }/PathTokenizer.cs (98%) rename src/Http/Routing/src/{Internal => }/RoutingMarkerService.cs (90%) create mode 100644 src/Http/Routing/src/Template/DefaultTemplateBinderFactory.cs create mode 100644 src/Http/Routing/src/Template/TemplateBinderFactory.cs rename src/Http/Routing/src/{Internal => Tree}/LinkGenerationDecisionTree.cs (98%) rename src/Http/Routing/src/{Internal => Tree}/OutboundMatchResult.cs (76%) rename src/Http/Routing/src/{Internal => }/UriBuilderContextPooledObjectPolicy.cs (62%) diff --git a/src/Http/Routing/ref/Microsoft.AspNetCore.Routing.netcoreapp3.0.cs b/src/Http/Routing/ref/Microsoft.AspNetCore.Routing.netcoreapp3.0.cs index b83aa2efeb..3827225df0 100644 --- a/src/Http/Routing/ref/Microsoft.AspNetCore.Routing.netcoreapp3.0.cs +++ b/src/Http/Routing/ref/Microsoft.AspNetCore.Routing.netcoreapp3.0.cs @@ -491,71 +491,19 @@ namespace Microsoft.AspNetCore.Routing.Constraints } namespace Microsoft.AspNetCore.Routing.Internal { - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] - public readonly partial struct BufferValue - { - private readonly object _dummy; - private readonly int _dummyPrimitive; - public BufferValue(string value, bool requiresEncoding) { throw null; } - public bool RequiresEncoding { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public string Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - } public partial class DfaGraphWriter { public DfaGraphWriter(System.IServiceProvider services) { } public void Write(Microsoft.AspNetCore.Routing.EndpointDataSource dataSource, System.IO.TextWriter writer) { } } - [System.Diagnostics.DebuggerDisplayAttribute("{DebuggerDisplayString,nq}")] - public partial class LinkGenerationDecisionTree - { - public LinkGenerationDecisionTree(System.Collections.Generic.IReadOnlyList entries) { } - public System.Collections.Generic.IList GetMatches(Microsoft.AspNetCore.Routing.RouteValueDictionary values, Microsoft.AspNetCore.Routing.RouteValueDictionary ambientValues) { throw null; } - } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] - public readonly partial struct OutboundMatchResult - { - private readonly object _dummy; - private readonly int _dummyPrimitive; - public OutboundMatchResult(Microsoft.AspNetCore.Routing.Tree.OutboundMatch match, bool isFallbackMatch) { throw null; } - public bool IsFallbackMatch { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public Microsoft.AspNetCore.Routing.Tree.OutboundMatch Match { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] - public partial struct PathTokenizer : System.Collections.Generic.IEnumerable, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlyList, System.Collections.IEnumerable - { - private object _dummy; - private int _dummyPrimitive; - public PathTokenizer(Microsoft.AspNetCore.Http.PathString path) { throw null; } - public int Count { get { throw null; } } - public Microsoft.Extensions.Primitives.StringSegment this[int index] { get { throw null; } } - public Microsoft.AspNetCore.Routing.Internal.PathTokenizer.Enumerator GetEnumerator() { throw null; } - System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() { throw null; } - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] - public partial struct Enumerator : System.Collections.Generic.IEnumerator, System.Collections.IEnumerator, System.IDisposable - { - private object _dummy; - private int _dummyPrimitive; - public Enumerator(Microsoft.AspNetCore.Routing.Internal.PathTokenizer tokenizer) { throw null; } - public Microsoft.Extensions.Primitives.StringSegment Current { get { throw null; } } - object System.Collections.IEnumerator.Current { get { throw null; } } - public void Dispose() { } - public bool MoveNext() { throw null; } - public void Reset() { } - } - } + [System.ObsoleteAttribute("This type will be marked as internal in a future release.")] public enum SegmentState { Beginning = 0, Inside = 1, } - public partial class UriBuilderContextPooledObjectPolicy : Microsoft.Extensions.ObjectPool.IPooledObjectPolicy - { - public UriBuilderContextPooledObjectPolicy() { } - public Microsoft.AspNetCore.Routing.Internal.UriBuildingContext Create() { throw null; } - public bool Return(Microsoft.AspNetCore.Routing.Internal.UriBuildingContext obj) { throw null; } - } [System.Diagnostics.DebuggerDisplayAttribute("{DebuggerToString(),nq}")] + [System.ObsoleteAttribute("This type will be marked as internal in a future release.")] public partial class UriBuildingContext { public UriBuildingContext(System.Text.Encodings.Web.UrlEncoder urlEncoder) { } @@ -808,13 +756,21 @@ namespace Microsoft.AspNetCore.Routing.Template } public partial class TemplateBinder { + [System.ObsoleteAttribute("This constructor is obsolete and will be marked internal in a future release. Use the TemplateBinderFactory service to create TemplateBinder instances.")] public TemplateBinder(System.Text.Encodings.Web.UrlEncoder urlEncoder, Microsoft.Extensions.ObjectPool.ObjectPool pool, Microsoft.AspNetCore.Routing.Patterns.RoutePattern pattern, Microsoft.AspNetCore.Routing.RouteValueDictionary defaults, System.Collections.Generic.IEnumerable requiredKeys, System.Collections.Generic.IEnumerable> parameterPolicies) { } + [System.ObsoleteAttribute("This constructor is obsolete and will be marked internal in a furture release. Use the TemplateBinderFactory service to create TemplateBinder instances.")] public TemplateBinder(System.Text.Encodings.Web.UrlEncoder urlEncoder, Microsoft.Extensions.ObjectPool.ObjectPool pool, Microsoft.AspNetCore.Routing.Template.RouteTemplate template, Microsoft.AspNetCore.Routing.RouteValueDictionary defaults) { } public string BindValues(Microsoft.AspNetCore.Routing.RouteValueDictionary acceptedValues) { throw null; } public Microsoft.AspNetCore.Routing.Template.TemplateValuesResult GetValues(Microsoft.AspNetCore.Routing.RouteValueDictionary ambientValues, Microsoft.AspNetCore.Routing.RouteValueDictionary values) { throw null; } public static bool RoutePartsEqual(object a, object b) { throw null; } public bool TryProcessConstraints(Microsoft.AspNetCore.Http.HttpContext httpContext, Microsoft.AspNetCore.Routing.RouteValueDictionary combinedValues, out string parameterName, out Microsoft.AspNetCore.Routing.IRouteConstraint constraint) { throw null; } } + public abstract partial class TemplateBinderFactory + { + protected TemplateBinderFactory() { } + public abstract Microsoft.AspNetCore.Routing.Template.TemplateBinder Create(Microsoft.AspNetCore.Routing.Patterns.RoutePattern pattern); + public abstract Microsoft.AspNetCore.Routing.Template.TemplateBinder Create(Microsoft.AspNetCore.Routing.Template.RouteTemplate template, Microsoft.AspNetCore.Routing.RouteValueDictionary defaults); + } public partial class TemplateMatcher { public TemplateMatcher(Microsoft.AspNetCore.Routing.Template.RouteTemplate template, Microsoft.AspNetCore.Routing.RouteValueDictionary defaults) { } @@ -901,6 +857,7 @@ namespace Microsoft.AspNetCore.Routing.Tree } public partial class TreeRouteBuilder { + [System.ObsoleteAttribute("This constructor will be marked internal in a future release. Use the service provider to create instances of TreeRouteBuilder.")] public TreeRouteBuilder(Microsoft.Extensions.Logging.ILoggerFactory loggerFactory, Microsoft.Extensions.ObjectPool.ObjectPool objectPool, Microsoft.AspNetCore.Routing.IInlineConstraintResolver constraintResolver) { } public System.Collections.Generic.IList InboundEntries { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } public System.Collections.Generic.IList OutboundEntries { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } @@ -913,6 +870,7 @@ namespace Microsoft.AspNetCore.Routing.Tree public partial class TreeRouter : Microsoft.AspNetCore.Routing.IRouter { public static readonly string RouteGroupKey; + [System.ObsoleteAttribute("This constructor will be marked obsolete in a future release. Use the TreeRouterBuilder to create instances of TreeRouter.")] public TreeRouter(Microsoft.AspNetCore.Routing.Tree.UrlMatchingTree[] trees, System.Collections.Generic.IEnumerable linkGenerationEntries, System.Text.Encodings.Web.UrlEncoder urlEncoder, Microsoft.Extensions.ObjectPool.ObjectPool objectPool, Microsoft.Extensions.Logging.ILogger routeLogger, Microsoft.Extensions.Logging.ILogger constraintLogger, int version) { } public int Version { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } public Microsoft.AspNetCore.Routing.VirtualPathData GetVirtualPath(Microsoft.AspNetCore.Routing.VirtualPathContext context) { throw null; } diff --git a/src/Http/Routing/src/Internal/ArrayBuilder.cs b/src/Http/Routing/src/ArrayBuilder.cs similarity index 97% rename from src/Http/Routing/src/Internal/ArrayBuilder.cs rename to src/Http/Routing/src/ArrayBuilder.cs index 868b3cfc27..ff20c2e64d 100644 --- a/src/Http/Routing/src/Internal/ArrayBuilder.cs +++ b/src/Http/Routing/src/ArrayBuilder.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -10,7 +10,7 @@ using System; using System.Diagnostics; -namespace Microsoft.AspNetCore.Routing.Internal +namespace Microsoft.AspNetCore.Routing { /// /// Helper type for avoiding allocations while building arrays. @@ -166,4 +166,4 @@ namespace Microsoft.AspNetCore.Routing.Internal _array = next; } } -} \ No newline at end of file +} diff --git a/src/Http/Routing/src/DefaultLinkGenerator.cs b/src/Http/Routing/src/DefaultLinkGenerator.cs index 21f36cac63..aae057a3ac 100644 --- a/src/Http/Routing/src/DefaultLinkGenerator.cs +++ b/src/Http/Routing/src/DefaultLinkGenerator.cs @@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Routing internal sealed class DefaultLinkGenerator : LinkGenerator, IDisposable { private readonly ParameterPolicyFactory _parameterPolicyFactory; - private readonly ObjectPool _uriBuildingContextPool; + private readonly TemplateBinderFactory _binderFactory; private readonly ILogger _logger; private readonly IServiceProvider _serviceProvider; @@ -38,14 +38,14 @@ namespace Microsoft.AspNetCore.Routing public DefaultLinkGenerator( ParameterPolicyFactory parameterPolicyFactory, + TemplateBinderFactory binderFactory, EndpointDataSource dataSource, - ObjectPool uriBuildingContextPool, IOptions routeOptions, ILogger logger, IServiceProvider serviceProvider) { _parameterPolicyFactory = parameterPolicyFactory; - _uriBuildingContextPool = uriBuildingContextPool; + _binderFactory = binderFactory; _logger = logger; _serviceProvider = serviceProvider; @@ -282,40 +282,7 @@ namespace Microsoft.AspNetCore.Routing private TemplateBinder CreateTemplateBinder(RouteEndpoint endpoint) { - // Now create the constraints and parameter transformers from the pattern - var policies = new List<(string parameterName, IParameterPolicy policy)>(); - foreach (var kvp in endpoint.RoutePattern.ParameterPolicies) - { - var parameterName = kvp.Key; - - // It's possible that we don't have an actual route parameter, we need to support that case. - var parameter = endpoint.RoutePattern.GetParameter(parameterName); - - // Use the first parameter transformer per parameter - var foundTransformer = false; - for (var i = 0; i < kvp.Value.Count; i++) - { - var parameterPolicy = _parameterPolicyFactory.Create(parameter, kvp.Value[i]); - if (!foundTransformer && parameterPolicy is IOutboundParameterTransformer parameterTransformer) - { - policies.Add((parameterName, parameterTransformer)); - foundTransformer = true; - } - - if (parameterPolicy is IRouteConstraint constraint) - { - policies.Add((parameterName, constraint)); - } - } - } - - return new TemplateBinder( - UrlEncoder.Default, - _uriBuildingContextPool, - endpoint.RoutePattern, - new RouteValueDictionary(endpoint.RoutePattern.Defaults), - endpoint.RoutePattern.RequiredValues.Keys, - policies); + return _binderFactory.Create(endpoint.RoutePattern); } // Internal for testing diff --git a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs index 94c1efdd26..8a9d10a45f 100644 --- a/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs +++ b/src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing.Internal; using Microsoft.AspNetCore.Routing.Matching; using Microsoft.AspNetCore.Routing.Patterns; +using Microsoft.AspNetCore.Routing.Template; using Microsoft.AspNetCore.Routing.Tree; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; @@ -34,6 +35,7 @@ namespace Microsoft.Extensions.DependencyInjection services.TryAddTransient(); services.TryAddTransient(); +#pragma warning disable CS0618 // Type or member is obsolete services.TryAddSingleton>(s => { var provider = s.GetRequiredService(); @@ -49,6 +51,7 @@ namespace Microsoft.Extensions.DependencyInjection var constraintResolver = s.GetRequiredService(); return new TreeRouteBuilder(loggerFactory, objectPool, constraintResolver); })); +#pragma warning restore CS0618 // Type or member is obsolete services.TryAddSingleton(typeof(RoutingMarkerService)); @@ -88,6 +91,7 @@ namespace Microsoft.Extensions.DependencyInjection // // Misc infrastructure // + services.TryAddSingleton(); services.TryAddSingleton(); return services; } diff --git a/src/Http/Routing/src/Internal/BufferValue.cs b/src/Http/Routing/src/Internal/BufferValue.cs deleted file mode 100644 index f71617038d..0000000000 --- a/src/Http/Routing/src/Internal/BufferValue.cs +++ /dev/null @@ -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. - -namespace Microsoft.AspNetCore.Routing.Internal -{ - public readonly struct BufferValue - { - public BufferValue(string value, bool requiresEncoding) - { - Value = value; - RequiresEncoding = requiresEncoding; - } - - public bool RequiresEncoding { get; } - - public string Value { get; } - } -} diff --git a/src/Http/Routing/src/Internal/SegmentState.cs b/src/Http/Routing/src/Internal/SegmentState.cs index 35076a0678..6431f01059 100644 --- a/src/Http/Routing/src/Internal/SegmentState.cs +++ b/src/Http/Routing/src/Internal/SegmentState.cs @@ -1,6 +1,8 @@ // 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; + namespace Microsoft.AspNetCore.Routing.Internal { // Segments are treated as all-or-none. We should never output a partial segment. @@ -9,6 +11,7 @@ namespace Microsoft.AspNetCore.Routing.Internal // used a value for {p1}, we have to output the entire segment up to the next "/". // Otherwise we could end up with the partial segment "v1" instead of the entire // segment "v1-v2.xml". + [Obsolete("This type will be marked as internal in a future release.")] public enum SegmentState { Beginning, diff --git a/src/Http/Routing/src/Internal/UriBuildingContext.cs b/src/Http/Routing/src/Internal/UriBuildingContext.cs index 82e0b4137b..dcfde1d866 100644 --- a/src/Http/Routing/src/Internal/UriBuildingContext.cs +++ b/src/Http/Routing/src/Internal/UriBuildingContext.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -10,6 +11,7 @@ using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Routing.Internal { + [Obsolete("This type will be marked as internal in a future release.")] [DebuggerDisplay("{DebuggerToString(),nq}")] public class UriBuildingContext { @@ -321,5 +323,18 @@ namespace Microsoft.AspNetCore.Routing.Internal { return string.Format("{{Accepted: '{0}' Buffered: '{1}'}}", _path, string.Join("", _buffer)); } + + private readonly struct BufferValue + { + public BufferValue(string value, bool requiresEncoding) + { + Value = value; + RequiresEncoding = requiresEncoding; + } + + public bool RequiresEncoding { get; } + + public string Value { get; } + } } } diff --git a/src/Http/Routing/src/Internal/NullRouter.cs b/src/Http/Routing/src/NullRouter.cs similarity index 100% rename from src/Http/Routing/src/Internal/NullRouter.cs rename to src/Http/Routing/src/NullRouter.cs diff --git a/src/Http/Routing/src/Internal/ParameterPolicyActivator.cs b/src/Http/Routing/src/ParameterPolicyActivator.cs similarity index 98% rename from src/Http/Routing/src/Internal/ParameterPolicyActivator.cs rename to src/Http/Routing/src/ParameterPolicyActivator.cs index c04de5ba60..ab18e7efe8 100644 --- a/src/Http/Routing/src/Internal/ParameterPolicyActivator.cs +++ b/src/Http/Routing/src/ParameterPolicyActivator.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -9,7 +9,7 @@ using System.Reflection; using System.Text; using Microsoft.Extensions.DependencyInjection; -namespace Microsoft.AspNetCore.Routing.Internal +namespace Microsoft.AspNetCore.Routing { internal static class ParameterPolicyActivator { diff --git a/src/Http/Routing/src/Internal/PathTokenizer.cs b/src/Http/Routing/src/PathTokenizer.cs similarity index 98% rename from src/Http/Routing/src/Internal/PathTokenizer.cs rename to src/Http/Routing/src/PathTokenizer.cs index 9418989fdb..307dfb1159 100644 --- a/src/Http/Routing/src/Internal/PathTokenizer.cs +++ b/src/Http/Routing/src/PathTokenizer.cs @@ -8,9 +8,9 @@ using System.Diagnostics; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; -namespace Microsoft.AspNetCore.Routing.Internal +namespace Microsoft.AspNetCore.Routing { - public struct PathTokenizer : IReadOnlyList + internal struct PathTokenizer : IReadOnlyList { private readonly string _path; private int _count; diff --git a/src/Http/Routing/src/RouteBase.cs b/src/Http/Routing/src/RouteBase.cs index 2659e7fac4..c12eaae75b 100644 --- a/src/Http/Routing/src/RouteBase.cs +++ b/src/Http/Routing/src/RouteBase.cs @@ -246,14 +246,16 @@ namespace Microsoft.AspNetCore.Routing } } +#pragma warning disable CS0618 // Type or member is obsolete private void EnsureBinder(HttpContext context) { if (_binder == null) { - var pool = context.RequestServices.GetRequiredService>(); - _binder = new TemplateBinder(UrlEncoder.Default, pool, ParsedTemplate, Defaults); + var binderFactory = context.RequestServices.GetRequiredService(); + _binder = binderFactory.Create(ParsedTemplate, Defaults); } } +#pragma warning restore CS0618 // Type or member is obsolete private void EnsureLoggers(HttpContext context) { diff --git a/src/Http/Routing/src/Internal/RoutingMarkerService.cs b/src/Http/Routing/src/RoutingMarkerService.cs similarity index 90% rename from src/Http/Routing/src/Internal/RoutingMarkerService.cs rename to src/Http/Routing/src/RoutingMarkerService.cs index c7bed9df18..ab876b0c39 100644 --- a/src/Http/Routing/src/Internal/RoutingMarkerService.cs +++ b/src/Http/Routing/src/RoutingMarkerService.cs @@ -3,7 +3,7 @@ using Microsoft.Extensions.DependencyInjection; -namespace Microsoft.AspNetCore.Routing.Internal +namespace Microsoft.AspNetCore.Routing { /// /// A marker class used to determine if all the routing services were added @@ -12,4 +12,4 @@ namespace Microsoft.AspNetCore.Routing.Internal internal class RoutingMarkerService { } -} \ No newline at end of file +} diff --git a/src/Http/Routing/src/Template/DefaultTemplateBinderFactory.cs b/src/Http/Routing/src/Template/DefaultTemplateBinderFactory.cs new file mode 100644 index 0000000000..86e33e04a3 --- /dev/null +++ b/src/Http/Routing/src/Template/DefaultTemplateBinderFactory.cs @@ -0,0 +1,95 @@ +// 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.Collections.Generic; +using System.Text.Encodings.Web; +using Microsoft.AspNetCore.Routing.Internal; +using Microsoft.AspNetCore.Routing.Patterns; +using Microsoft.Extensions.ObjectPool; + +namespace Microsoft.AspNetCore.Routing.Template +{ + internal sealed class DefaultTemplateBinderFactory : TemplateBinderFactory + { + private readonly ParameterPolicyFactory _policyFactory; +#pragma warning disable CS0618 // Type or member is obsolete + private readonly ObjectPool _pool; +#pragma warning restore CS0618 // Type or member is obsolete + + public DefaultTemplateBinderFactory( + ParameterPolicyFactory policyFactory, +#pragma warning disable CS0618 // Type or member is obsolete + ObjectPool pool) +#pragma warning restore CS0618 // Type or member is obsolete + { + if (policyFactory == null) + { + throw new ArgumentNullException(nameof(policyFactory)); + } + + if (pool == null) + { + throw new ArgumentNullException(nameof(pool)); + } + + _policyFactory = policyFactory; + _pool = pool; + + } + + public override TemplateBinder Create(RouteTemplate template, RouteValueDictionary defaults) + { + if (template == null) + { + throw new ArgumentNullException(nameof(template)); + } + + if (defaults == null) + { + throw new ArgumentNullException(nameof(defaults)); + } + +#pragma warning disable CS0618 // Type or member is obsolete + return new TemplateBinder(UrlEncoder.Default, _pool, template, defaults); +#pragma warning restore CS0618 // Type or member is obsolete + } + + public override TemplateBinder Create(RoutePattern pattern) + { + if (pattern == null) + { + throw new ArgumentNullException(nameof(pattern)); + } + + // Now create the constraints and parameter transformers from the pattern + var policies = new List<(string parameterName, IParameterPolicy policy)>(); + foreach (var kvp in pattern.ParameterPolicies) + { + var parameterName = kvp.Key; + + // It's possible that we don't have an actual route parameter, we need to support that case. + var parameter = pattern.GetParameter(parameterName); + + // Use the first parameter transformer per parameter + var foundTransformer = false; + for (var i = 0; i < kvp.Value.Count; i++) + { + var parameterPolicy = _policyFactory.Create(parameter, kvp.Value[i]); + if (!foundTransformer && parameterPolicy is IOutboundParameterTransformer parameterTransformer) + { + policies.Add((parameterName, parameterTransformer)); + foundTransformer = true; + } + + if (parameterPolicy is IRouteConstraint constraint) + { + policies.Add((parameterName, constraint)); + } + } + } + + return new TemplateBinder(UrlEncoder.Default, _pool, pattern, policies); + } + } +} diff --git a/src/Http/Routing/src/Template/TemplateBinder.cs b/src/Http/Routing/src/Template/TemplateBinder.cs index c78573ab98..efc46f3781 100644 --- a/src/Http/Routing/src/Template/TemplateBinder.cs +++ b/src/Http/Routing/src/Template/TemplateBinder.cs @@ -19,7 +19,9 @@ namespace Microsoft.AspNetCore.Routing.Template public class TemplateBinder { private readonly UrlEncoder _urlEncoder; +#pragma warning disable CS0618 // Type or member is obsolete private readonly ObjectPool _pool; +#pragma warning restore CS0618 // Type or member is obsolete private readonly (string parameterName, IRouteConstraint constraint)[] _constraints; private readonly RouteValueDictionary _defaults; @@ -40,6 +42,9 @@ namespace Microsoft.AspNetCore.Routing.Template /// The . /// The to bind values to. /// The default values for . + [Obsolete( + "This constructor is obsolete and will be marked internal in a furture release. Use the TemplateBinderFactory service " + + "to create TemplateBinder instances.")] public TemplateBinder( UrlEncoder urlEncoder, ObjectPool pool, @@ -60,6 +65,9 @@ namespace Microsoft.AspNetCore.Routing.Template /// /// A list of (, ) pairs to evalute when producing a URI. /// + [Obsolete( + "This constructor is obsolete and will be marked internal in a future release. Use the TemplateBinderFactory service " + + "to create TemplateBinder instances.")] public TemplateBinder( UrlEncoder urlEncoder, ObjectPool pool, @@ -110,6 +118,58 @@ namespace Microsoft.AspNetCore.Routing.Template _slots = AssignSlots(_pattern, _filters); } + internal TemplateBinder( + UrlEncoder urlEncoder, +#pragma warning disable CS0618 // Type or member is obsolete + ObjectPool pool, +#pragma warning restore CS0618 // Type or member is obsolete + RoutePattern pattern, + IEnumerable<(string parameterName, IParameterPolicy policy)> parameterPolicies) + { + if (urlEncoder == null) + { + throw new ArgumentNullException(nameof(urlEncoder)); + } + + if (pool == null) + { + throw new ArgumentNullException(nameof(pool)); + } + + if (pattern == null) + { + throw new ArgumentNullException(nameof(pattern)); + } + + // Parameter policies can be null. + + _urlEncoder = urlEncoder; + _pool = pool; + _pattern = pattern; + _defaults = new RouteValueDictionary(pattern.Defaults); + _requiredKeys = pattern.RequiredValues.Keys.ToArray(); + + // Any default that doesn't have a corresponding parameter is a 'filter' and if a value + // is provided for that 'filter' it must match the value in defaults. + var filters = new RouteValueDictionary(_defaults); + for (var i = 0; i < pattern.Parameters.Count; i++) + { + filters.Remove(pattern.Parameters[i].Name); + } + _filters = filters.ToArray(); + + _constraints = parameterPolicies + ?.Where(p => p.policy is IRouteConstraint) + .Select(p => (p.parameterName, (IRouteConstraint)p.policy)) + .ToArray() ?? Array.Empty<(string, IRouteConstraint)>(); + _parameterTransformers = parameterPolicies + ?.Where(p => p.policy is IOutboundParameterTransformer) + .Select(p => (p.parameterName, (IOutboundParameterTransformer)p.policy)) + .ToArray() ?? Array.Empty<(string, IOutboundParameterTransformer)>(); + + _slots = AssignSlots(_pattern, _filters); + } + // Step 1: Get the list of values we're going to try to use to match and generate this URI public TemplateValuesResult GetValues(RouteValueDictionary ambientValues, RouteValueDictionary values) { @@ -442,6 +502,7 @@ namespace Microsoft.AspNetCore.Routing.Template } } +#pragma warning disable CS0618 // Type or member is obsolete private bool TryBindValuesCore(UriBuildingContext context, RouteValueDictionary acceptedValues) { // If we have any output parameter transformers, allow them a chance to influence the parameter values @@ -578,6 +639,7 @@ namespace Microsoft.AspNetCore.Routing.Template } return false; } +#pragma warning restore CS0618 // Type or member is obsolete /// /// Compares two objects for equality as parts of a case-insensitive path. diff --git a/src/Http/Routing/src/Template/TemplateBinderFactory.cs b/src/Http/Routing/src/Template/TemplateBinderFactory.cs new file mode 100644 index 0000000000..273b4c4a68 --- /dev/null +++ b/src/Http/Routing/src/Template/TemplateBinderFactory.cs @@ -0,0 +1,29 @@ +// 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.Routing.Patterns; + +namespace Microsoft.AspNetCore.Routing.Template +{ + /// + /// A factory used to create instances. + /// + public abstract class TemplateBinderFactory + { + /// + /// Creates a new from the provided and + /// . + /// + /// The route template. + /// A collection of extra default values that do not appear in the route template. + /// A . + public abstract TemplateBinder Create(RouteTemplate template, RouteValueDictionary defaults); + + /// + /// Creates a new from the provided . + /// + /// The . + /// A . + public abstract TemplateBinder Create(RoutePattern pattern); + } +} diff --git a/src/Http/Routing/src/Internal/LinkGenerationDecisionTree.cs b/src/Http/Routing/src/Tree/LinkGenerationDecisionTree.cs similarity index 98% rename from src/Http/Routing/src/Internal/LinkGenerationDecisionTree.cs rename to src/Http/Routing/src/Tree/LinkGenerationDecisionTree.cs index effc1f0b24..8b8315285f 100644 --- a/src/Http/Routing/src/Internal/LinkGenerationDecisionTree.cs +++ b/src/Http/Routing/src/Tree/LinkGenerationDecisionTree.cs @@ -8,13 +8,12 @@ using System.Linq; using System.Text; using Microsoft.AspNetCore.Routing.DecisionTree; using Microsoft.AspNetCore.Routing.Patterns; -using Microsoft.AspNetCore.Routing.Tree; -namespace Microsoft.AspNetCore.Routing.Internal +namespace Microsoft.AspNetCore.Routing.Tree { // A decision tree that matches link generation entries based on route data. [DebuggerDisplay("{DebuggerDisplayString,nq}")] - public class LinkGenerationDecisionTree + internal class LinkGenerationDecisionTree { // Fallback value for cases where the ambient values weren't provided. // diff --git a/src/Http/Routing/src/Internal/OutboundMatchResult.cs b/src/Http/Routing/src/Tree/OutboundMatchResult.cs similarity index 76% rename from src/Http/Routing/src/Internal/OutboundMatchResult.cs rename to src/Http/Routing/src/Tree/OutboundMatchResult.cs index 1b221013ba..ba19555524 100644 --- a/src/Http/Routing/src/Internal/OutboundMatchResult.cs +++ b/src/Http/Routing/src/Tree/OutboundMatchResult.cs @@ -1,11 +1,9 @@ // 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.Routing.Tree; - -namespace Microsoft.AspNetCore.Routing.Internal +namespace Microsoft.AspNetCore.Routing.Tree { - public readonly struct OutboundMatchResult + internal readonly struct OutboundMatchResult { public OutboundMatchResult(OutboundMatch match, bool isFallbackMatch) { diff --git a/src/Http/Routing/src/Tree/TreeRouteBuilder.cs b/src/Http/Routing/src/Tree/TreeRouteBuilder.cs index a8a11d803b..ba4bdefdfa 100644 --- a/src/Http/Routing/src/Tree/TreeRouteBuilder.cs +++ b/src/Http/Routing/src/Tree/TreeRouteBuilder.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Text.Encodings.Web; using Microsoft.AspNetCore.Routing.Internal; @@ -21,7 +20,9 @@ namespace Microsoft.AspNetCore.Routing.Tree private readonly ILogger _logger; private readonly ILogger _constraintLogger; private readonly UrlEncoder _urlEncoder; +#pragma warning disable CS0618 // Type or member is obsolete private readonly ObjectPool _objectPool; +#pragma warning restore CS0618 // Type or member is obsolete private readonly IInlineConstraintResolver _constraintResolver; /// @@ -30,9 +31,12 @@ namespace Microsoft.AspNetCore.Routing.Tree /// The . /// The . /// The . + [Obsolete("This constructor will be marked internal in a future release. Use the service provider to create instances of TreeRouteBuilder.")] public TreeRouteBuilder( ILoggerFactory loggerFactory, +#pragma warning disable CS0618 // Type or member is obsolete ObjectPool objectPool, +#pragma warning restore CS0618 // Type or member is obsolete IInlineConstraintResolver constraintResolver) { if (loggerFactory == null) @@ -240,6 +244,7 @@ namespace Microsoft.AspNetCore.Routing.Tree tree.AddEntry(entry); } +#pragma warning disable CS0618 // Type or member is obsolete return new TreeRouter( trees.Values.OrderBy(tree => tree.Order).ToArray(), OutboundEntries, @@ -248,6 +253,7 @@ namespace Microsoft.AspNetCore.Routing.Tree _logger, _constraintLogger, version); +#pragma warning restore CS0618 // Type or member is obsolete } /// diff --git a/src/Http/Routing/src/Tree/TreeRouter.cs b/src/Http/Routing/src/Tree/TreeRouter.cs index 47fbf3802e..46828b94dd 100644 --- a/src/Http/Routing/src/Tree/TreeRouter.cs +++ b/src/Http/Routing/src/Tree/TreeRouter.cs @@ -40,11 +40,14 @@ namespace Microsoft.AspNetCore.Routing.Tree /// The instance used /// in . /// The version of this route. + [Obsolete("This constructor will be marked obsolete in a future release. Use the TreeRouterBuilder to create instances of TreeRouter.")] public TreeRouter( UrlMatchingTree[] trees, IEnumerable linkGenerationEntries, UrlEncoder urlEncoder, +#pragma warning disable CS0618 // Type or member is obsolete ObjectPool objectPool, +#pragma warning restore CS0618 // Type or member is obsolete ILogger routeLogger, ILogger constraintLogger, int version) diff --git a/src/Http/Routing/src/Internal/UriBuilderContextPooledObjectPolicy.cs b/src/Http/Routing/src/UriBuilderContextPooledObjectPolicy.cs similarity index 62% rename from src/Http/Routing/src/Internal/UriBuilderContextPooledObjectPolicy.cs rename to src/Http/Routing/src/UriBuilderContextPooledObjectPolicy.cs index 953d6a86c4..7ed7021d1f 100644 --- a/src/Http/Routing/src/Internal/UriBuilderContextPooledObjectPolicy.cs +++ b/src/Http/Routing/src/UriBuilderContextPooledObjectPolicy.cs @@ -2,11 +2,13 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Text.Encodings.Web; +using Microsoft.AspNetCore.Routing.Internal; using Microsoft.Extensions.ObjectPool; -namespace Microsoft.AspNetCore.Routing.Internal +namespace Microsoft.AspNetCore.Routing { - public class UriBuilderContextPooledObjectPolicy : IPooledObjectPolicy +#pragma warning disable CS0618 // Type or member is obsolete + internal class UriBuilderContextPooledObjectPolicy : IPooledObjectPolicy { public UriBuildingContext Create() { @@ -19,4 +21,5 @@ namespace Microsoft.AspNetCore.Routing.Internal return true; } } +#pragma warning restore CS0618 // Type or member is obsolete } diff --git a/src/Http/Routing/test/UnitTests/Internal/UriBuildingContextTest.cs b/src/Http/Routing/test/UnitTests/Internal/UriBuildingContextTest.cs index cfd1531a5d..f9f0be2ab8 100644 --- a/src/Http/Routing/test/UnitTests/Internal/UriBuildingContextTest.cs +++ b/src/Http/Routing/test/UnitTests/Internal/UriBuildingContextTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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.Extensions.WebEncoders.Testing; @@ -6,6 +6,7 @@ using Xunit; namespace Microsoft.AspNetCore.Routing.Internal { +#pragma warning disable CS0618 // Type or member is obsolete public class UriBuildingContextTest { [Fact] @@ -16,7 +17,7 @@ namespace Microsoft.AspNetCore.Routing.Internal var value = "a/b b1/c"; var expected = "/UrlEncode[[a/b b1/c]]"; var uriBuilldingContext = new UriBuildingContext(urlTestEncoder); - + // Act uriBuilldingContext.EncodeValue(value, 0, value.Length, encodeSlashes: true); @@ -97,4 +98,5 @@ namespace Microsoft.AspNetCore.Routing.Internal Assert.Equal(expected, uriBuilldingContext.ToPathString().Value); } } +#pragma warning restore CS0618 // Type or member is obsolete } diff --git a/src/Http/Routing/test/UnitTests/LinkGeneratorTestBase.cs b/src/Http/Routing/test/UnitTests/LinkGeneratorTestBase.cs index 202238e5b8..19ded38c8b 100644 --- a/src/Http/Routing/test/UnitTests/LinkGeneratorTestBase.cs +++ b/src/Http/Routing/test/UnitTests/LinkGeneratorTestBase.cs @@ -5,6 +5,7 @@ using System; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Routing.Internal; +using Microsoft.AspNetCore.Routing.Template; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.ObjectPool; @@ -82,8 +83,8 @@ namespace Microsoft.AspNetCore.Routing return new DefaultLinkGenerator( new DefaultParameterPolicyFactory(routeOptions, serviceProvider), + serviceProvider.GetRequiredService(), new CompositeEndpointDataSource(routeOptions.Value.EndpointDataSources), - new DefaultObjectPool(new UriBuilderContextPooledObjectPolicy()), routeOptions, NullLogger.Instance, serviceProvider); diff --git a/src/Http/Routing/test/UnitTests/Matching/TreeRouterMatcherBuilder.cs b/src/Http/Routing/test/UnitTests/Matching/TreeRouterMatcherBuilder.cs index b6346b0f14..5d4e25dfff 100644 --- a/src/Http/Routing/test/UnitTests/Matching/TreeRouterMatcherBuilder.cs +++ b/src/Http/Routing/test/UnitTests/Matching/TreeRouterMatcherBuilder.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -32,10 +32,12 @@ namespace Microsoft.AspNetCore.Routing.Matching public override Matcher Build() { +#pragma warning disable CS0618 // Type or member is obsolete var builder = new TreeRouteBuilder( NullLoggerFactory.Instance, new DefaultObjectPool(new UriBuilderContextPooledObjectPolicy()), new DefaultInlineConstraintResolver(Options.Create(new RouteOptions()), new TestServiceProvider())); +#pragma warning restore CS0618 // Type or member is obsolete var selector = new DefaultEndpointSelector(); diff --git a/src/Http/Routing/test/UnitTests/RouteTest.cs b/src/Http/Routing/test/UnitTests/RouteTest.cs index 4c0f647caf..db7bce321e 100644 --- a/src/Http/Routing/test/UnitTests/RouteTest.cs +++ b/src/Http/Routing/test/UnitTests/RouteTest.cs @@ -658,6 +658,7 @@ namespace Microsoft.AspNetCore.Routing var expected = "/Home/Index?name=" + UrlEncoder.Default.Encode(nameRouteValue); var services = new ServiceCollection(); services.AddSingleton(NullLoggerFactory.Instance); + services.AddOptions(); services.AddRouting(); // This test encoder should not be used by Routing and should always use the default one. services.AddSingleton(new UrlTestEncoder()); @@ -1520,6 +1521,7 @@ namespace Microsoft.AspNetCore.Routing { var services = new ServiceCollection(); services.AddSingleton(NullLoggerFactory.Instance); + services.AddOptions(); services.AddRouting(); var context = new DefaultHttpContext diff --git a/src/Http/Routing/test/UnitTests/Template/TemplateBinderTests.cs b/src/Http/Routing/test/UnitTests/Template/TemplateBinderTests.cs index f5f497e4b2..0ae7eb7e5b 100644 --- a/src/Http/Routing/test/UnitTests/Template/TemplateBinderTests.cs +++ b/src/Http/Routing/test/UnitTests/Template/TemplateBinderTests.cs @@ -16,6 +16,7 @@ using Xunit; namespace Microsoft.AspNetCore.Routing.Template.Tests { +#pragma warning disable CS0618 // Type or member is obsolete public class TemplateBinderTests { private readonly IInlineConstraintResolver _inlineConstraintResolver = GetInlineConstraintResolver(); @@ -1480,4 +1481,5 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests public Dictionary Parameters { get; private set; } } } +#pragma warning restore CS0618 // Type or member is obsolete } diff --git a/src/Http/Routing/test/UnitTests/Tree/TreeRouteBuilderTest.cs b/src/Http/Routing/test/UnitTests/Tree/TreeRouteBuilderTest.cs index dc9d2c7594..665e11f07e 100644 --- a/src/Http/Routing/test/UnitTests/Tree/TreeRouteBuilderTest.cs +++ b/src/Http/Routing/test/UnitTests/Tree/TreeRouteBuilderTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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.Text.Encodings.Web; @@ -248,10 +248,12 @@ namespace Microsoft.AspNetCore.Routing.Tree var objectPool = objectPoolProvider.Create(objectPolicy); var constraintResolver = GetInlineConstraintResolver(); +#pragma warning disable CS0618 // Type or member is obsolete var builder = new TreeRouteBuilder( NullLoggerFactory.Instance, objectPool, constraintResolver); +#pragma warning restore CS0618 // Type or member is obsolete return builder; } diff --git a/src/Http/Routing/test/UnitTests/Tree/TreeRouterTest.cs b/src/Http/Routing/test/UnitTests/Tree/TreeRouterTest.cs index b655941c00..d8bd30ae4f 100644 --- a/src/Http/Routing/test/UnitTests/Tree/TreeRouterTest.cs +++ b/src/Http/Routing/test/UnitTests/Tree/TreeRouterTest.cs @@ -23,8 +23,10 @@ namespace Microsoft.AspNetCore.Routing.Tree { private static readonly RequestDelegate NullHandler = (c) => Task.CompletedTask; +#pragma warning disable CS0618 // Type or member is obsolete private static ObjectPool Pool = new DefaultObjectPoolProvider().Create( new UriBuilderContextPooledObjectPolicy()); +#pragma warning restore CS0618 // Type or member is obsolete [Theory] [InlineData("template/5", "template/{parameter:int}")] @@ -2073,6 +2075,7 @@ namespace Microsoft.AspNetCore.Routing.Tree return new DefaultInlineConstraintResolver(optionsMock.Object, new TestServiceProvider()); } +#pragma warning disable CS0618 // Type or member is obsolete private static TreeRouteBuilder CreateBuilder() { var objectPoolProvider = new DefaultObjectPoolProvider(); @@ -2086,6 +2089,7 @@ namespace Microsoft.AspNetCore.Routing.Tree constraintResolver); return builder; } +#pragma warning restore CS0618 // Type or member is obsolete private static TreeRouter CreateTreeRouter( string firstTemplate,