Add IParameterTransformer (#750)
This commit is contained in:
parent
e5cc4564cb
commit
cee960f3c5
|
|
@ -0,0 +1,18 @@
|
|||
// 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>
|
||||
/// Defines the contract that a class must implement to transform parameter values.
|
||||
/// </summary>
|
||||
public interface IParameterTransformer : IParameterPolicy
|
||||
{
|
||||
/// <summary>
|
||||
/// Transforms the specified parameter value.
|
||||
/// </summary>
|
||||
/// <param name="value">The parameter value to transform.</param>
|
||||
/// <returns>The transformed value.</returns>
|
||||
string Transform(string value);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// 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.Constraints
|
||||
{
|
||||
internal class NullRouteConstraint : IRouteConstraint
|
||||
{
|
||||
public static readonly NullRouteConstraint Instance = new NullRouteConstraint();
|
||||
|
||||
private NullRouteConstraint()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Http;
|
|||
namespace Microsoft.AspNetCore.Routing.Constraints
|
||||
{
|
||||
/// <summary>
|
||||
/// Constrains a route parameter to contain only a specified strign.
|
||||
/// Constrains a route parameter to contain only a specified string.
|
||||
/// </summary>
|
||||
public class StringRouteConstraint : IRouteConstraint
|
||||
{
|
||||
|
|
|
|||
|
|
@ -56,7 +56,12 @@ namespace Microsoft.AspNetCore.Routing
|
|||
throw new ArgumentNullException(nameof(inlineConstraint));
|
||||
}
|
||||
|
||||
return ParameterPolicyActivator.ResolveParameterPolicy<IRouteConstraint>(_inlineConstraintMap, _serviceProvider, inlineConstraint, out _);
|
||||
// This will return null if the text resolves to a non-IRouteConstraint
|
||||
return ParameterPolicyActivator.ResolveParameterPolicy<IRouteConstraint>(
|
||||
_inlineConstraintMap,
|
||||
_serviceProvider,
|
||||
inlineConstraint,
|
||||
out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -271,7 +271,8 @@ namespace Microsoft.AspNetCore.Routing
|
|||
UrlEncoder.Default,
|
||||
_uriBuildingContextPool,
|
||||
endpoint.RoutePattern,
|
||||
new RouteValueDictionary(endpoint.RoutePattern.Defaults));
|
||||
new RouteValueDictionary(endpoint.RoutePattern.Defaults),
|
||||
_parameterPolicyFactory);
|
||||
|
||||
var routeValuesAddressMetadata = endpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
|
||||
var templateValuesResult = templateBinder.GetValues(
|
||||
|
|
|
|||
|
|
@ -44,7 +44,12 @@ namespace Microsoft.AspNetCore.Routing
|
|||
throw new ArgumentNullException(nameof(inlineText));
|
||||
}
|
||||
|
||||
var parameterPolicy = ParameterPolicyActivator.ResolveParameterPolicy<IParameterPolicy>(_options.ConstraintMap, _serviceProvider, inlineText, out var parameterPolicyKey);
|
||||
var parameterPolicy = ParameterPolicyActivator.ResolveParameterPolicy<IParameterPolicy>(
|
||||
_options.ConstraintMap,
|
||||
_serviceProvider,
|
||||
inlineText,
|
||||
out var parameterPolicyKey);
|
||||
|
||||
if (parameterPolicy == null)
|
||||
{
|
||||
throw new InvalidOperationException(Resources.FormatRoutePattern_ConstraintReferenceNotFound(
|
||||
|
|
|
|||
|
|
@ -13,7 +13,11 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
|||
{
|
||||
internal static class ParameterPolicyActivator
|
||||
{
|
||||
public static T ResolveParameterPolicy<T>(IDictionary<string, Type> inlineParameterPolicyMap, IServiceProvider serviceProvider, string inlineParameterPolicy, out string parameterPolicyKey)
|
||||
public static T ResolveParameterPolicy<T>(
|
||||
IDictionary<string, Type> inlineParameterPolicyMap,
|
||||
IServiceProvider serviceProvider,
|
||||
string inlineParameterPolicy,
|
||||
out string parameterPolicyKey)
|
||||
where T : IParameterPolicy
|
||||
{
|
||||
// IServiceProvider could be null
|
||||
|
|
@ -51,9 +55,18 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
|||
|
||||
if (!typeof(T).IsAssignableFrom(parameterPolicyType))
|
||||
{
|
||||
throw new RouteCreationException(
|
||||
Resources.FormatDefaultInlineConstraintResolver_TypeNotConstraint(
|
||||
parameterPolicyType, parameterPolicyKey, typeof(T).Name));
|
||||
if (!typeof(IParameterPolicy).IsAssignableFrom(parameterPolicyType))
|
||||
{
|
||||
// Error if type is not a parameter policy
|
||||
throw new RouteCreationException(
|
||||
Resources.FormatDefaultInlineConstraintResolver_TypeNotConstraint(
|
||||
parameterPolicyType, parameterPolicyKey, typeof(T).Name));
|
||||
}
|
||||
|
||||
// Return null if type is parameter policy but is not the exact type
|
||||
// This is used by IInlineConstraintResolver for backwards compatibility
|
||||
// e.g. looking for an IRouteConstraint but get a different IParameterPolicy type
|
||||
return default;
|
||||
}
|
||||
|
||||
try
|
||||
|
|
|
|||
|
|
@ -56,10 +56,10 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
|||
|
||||
public bool Accept(string value)
|
||||
{
|
||||
return Accept(value, encodeSlashes: true);
|
||||
return Accept(value, encodeSlashes: true, parameterTransformer: null);
|
||||
}
|
||||
|
||||
public bool Accept(string value, bool encodeSlashes)
|
||||
public bool Accept(string value, bool encodeSlashes, IParameterTransformer parameterTransformer)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
|
|
@ -117,11 +117,11 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
|||
if (_path.Length == 0 && value.Length > 0 && value[0] == '/')
|
||||
{
|
||||
_path.Append("/");
|
||||
EncodeValue(value, 1, value.Length - 1, encodeSlashes);
|
||||
EncodeValue(value, 1, value.Length - 1, encodeSlashes, parameterTransformer);
|
||||
}
|
||||
else
|
||||
{
|
||||
EncodeValue(value, encodeSlashes);
|
||||
EncodeValue(value, encodeSlashes, parameterTransformer);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -263,17 +263,24 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
|||
|
||||
private void EncodeValue(string value)
|
||||
{
|
||||
EncodeValue(value, encodeSlashes: true);
|
||||
EncodeValue(value, encodeSlashes: true, parameterTransformer: null);
|
||||
}
|
||||
|
||||
private void EncodeValue(string value, bool encodeSlashes)
|
||||
private void EncodeValue(string value, bool encodeSlashes, IParameterTransformer parameterTransformer)
|
||||
{
|
||||
EncodeValue(value, start: 0, characterCount: value.Length, encodeSlashes);
|
||||
EncodeValue(value, start: 0, characterCount: value.Length, encodeSlashes, parameterTransformer);
|
||||
}
|
||||
|
||||
// For testing
|
||||
internal void EncodeValue(string value, int start, int characterCount, bool encodeSlashes)
|
||||
internal void EncodeValue(string value, int start, int characterCount, bool encodeSlashes, IParameterTransformer parameterTransformer)
|
||||
{
|
||||
if (parameterTransformer != null)
|
||||
{
|
||||
value = parameterTransformer.Transform(value.Substring(0, characterCount));
|
||||
start = 0;
|
||||
characterCount = value.Length;
|
||||
}
|
||||
|
||||
// Just encode everything if its ok to encode slashes
|
||||
if (encodeSlashes)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.Routing.Constraints;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNetCore.Builder
|
||||
|
|
@ -107,10 +108,6 @@ namespace Microsoft.AspNetCore.Builder
|
|||
throw new RouteCreationException(Resources.FormatDefaultHandler_MustBeSet(nameof(IRouteBuilder)));
|
||||
}
|
||||
|
||||
var inlineConstraintResolver = routeBuilder
|
||||
.ServiceProvider
|
||||
.GetRequiredService<IInlineConstraintResolver>();
|
||||
|
||||
routeBuilder.Routes.Add(new Route(
|
||||
routeBuilder.DefaultHandler,
|
||||
name,
|
||||
|
|
@ -118,9 +115,52 @@ namespace Microsoft.AspNetCore.Builder
|
|||
new RouteValueDictionary(defaults),
|
||||
new RouteValueDictionary(constraints),
|
||||
new RouteValueDictionary(dataTokens),
|
||||
inlineConstraintResolver));
|
||||
CreateInlineConstraintResolver(routeBuilder.ServiceProvider)));
|
||||
|
||||
return routeBuilder;
|
||||
}
|
||||
|
||||
private static IInlineConstraintResolver CreateInlineConstraintResolver(IServiceProvider serviceProvider)
|
||||
{
|
||||
var inlineConstraintResolver = serviceProvider
|
||||
.GetRequiredService<IInlineConstraintResolver>();
|
||||
|
||||
var parameterPolicyFactory = serviceProvider
|
||||
.GetRequiredService<ParameterPolicyFactory>();
|
||||
|
||||
// This inline constraint resolver will return a null constraint for non-IRouteConstraint
|
||||
// parameter policies so Route does not error
|
||||
return new BackCompatInlineConstraintResolver(inlineConstraintResolver, parameterPolicyFactory);
|
||||
}
|
||||
|
||||
private class BackCompatInlineConstraintResolver : IInlineConstraintResolver
|
||||
{
|
||||
private readonly IInlineConstraintResolver _inner;
|
||||
private readonly ParameterPolicyFactory _parameterPolicyFactory;
|
||||
|
||||
public BackCompatInlineConstraintResolver(IInlineConstraintResolver inner, ParameterPolicyFactory parameterPolicyFactory)
|
||||
{
|
||||
_inner = inner;
|
||||
_parameterPolicyFactory = parameterPolicyFactory;
|
||||
}
|
||||
|
||||
public IRouteConstraint ResolveConstraint(string inlineConstraint)
|
||||
{
|
||||
var routeConstraint = _inner.ResolveConstraint(inlineConstraint);
|
||||
if (routeConstraint != null)
|
||||
{
|
||||
return routeConstraint;
|
||||
}
|
||||
|
||||
var parameterPolicy = _parameterPolicyFactory.Create(null, inlineConstraint);
|
||||
if (parameterPolicy != null)
|
||||
{
|
||||
// Logic inside Route will skip adding NullRouteConstraint
|
||||
return NullRouteConstraint.Instance;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Routing.Constraints;
|
||||
using Microsoft.AspNetCore.Routing.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
|
|
@ -158,6 +159,11 @@ namespace Microsoft.AspNetCore.Routing
|
|||
_displayName,
|
||||
_inlineConstraintResolver.GetType().Name));
|
||||
}
|
||||
else if (constraint == NullRouteConstraint.Instance)
|
||||
{
|
||||
// A null route constraint can be returned for other parameter policy types
|
||||
return;
|
||||
}
|
||||
|
||||
Add(key, constraint);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
private readonly ObjectPool<UriBuildingContext> _pool;
|
||||
|
||||
private readonly RouteValueDictionary _defaults;
|
||||
private readonly ParameterPolicyFactory _parameterPolicyFactory;
|
||||
private readonly RouteValueDictionary _filters;
|
||||
private readonly RoutePattern _pattern;
|
||||
|
||||
|
|
@ -35,7 +36,7 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
ObjectPool<UriBuildingContext> pool,
|
||||
RouteTemplate template,
|
||||
RouteValueDictionary defaults)
|
||||
: this(urlEncoder, pool, template?.ToRoutePattern(), defaults)
|
||||
: this(urlEncoder, pool, template?.ToRoutePattern(), defaults, parameterPolicyFactory: null)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -46,11 +47,13 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
/// <param name="pool">The <see cref="ObjectPool{T}"/>.</param>
|
||||
/// <param name="pattern">The <see cref="RoutePattern"/> to bind values to.</param>
|
||||
/// <param name="defaults">The default values for <paramref name="pattern"/>.</param>
|
||||
/// <param name="parameterPolicyFactory">The <see cref="ParameterPolicyFactory"/>.</param>
|
||||
public TemplateBinder(
|
||||
UrlEncoder urlEncoder,
|
||||
ObjectPool<UriBuildingContext> pool,
|
||||
RoutePattern pattern,
|
||||
RouteValueDictionary defaults)
|
||||
RouteValueDictionary defaults,
|
||||
ParameterPolicyFactory parameterPolicyFactory)
|
||||
{
|
||||
if (urlEncoder == null)
|
||||
{
|
||||
|
|
@ -71,6 +74,7 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
_pool = pool;
|
||||
_pattern = pattern;
|
||||
_defaults = defaults;
|
||||
_parameterPolicyFactory = parameterPolicyFactory;
|
||||
|
||||
// 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.
|
||||
|
|
@ -265,7 +269,7 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
// Example: template = {id}.{format?}. parameters: id=5
|
||||
// In this case after we have generated "5.", we wont find any value
|
||||
// for format, so we remove '.' and generate 5.
|
||||
if (!context.Accept(converted, parameterPart.EncodeSlashes))
|
||||
if (!context.Accept(converted, parameterPart.EncodeSlashes, GetParameterTransformer(parameterPart)))
|
||||
{
|
||||
if (j != 0 && parameterPart.IsOptional && (separatorPart = segment.Parts[j - 1] as RoutePatternSeparatorPart) != null)
|
||||
{
|
||||
|
|
@ -310,6 +314,26 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
return true;
|
||||
}
|
||||
|
||||
private IParameterTransformer GetParameterTransformer(RoutePatternParameterPart parameterPart)
|
||||
{
|
||||
if (_parameterPolicyFactory == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (var i = 0; i < parameterPart.ParameterPolicies.Count; i++)
|
||||
{
|
||||
// Use the first parameter transformer
|
||||
var parameterPolicy = _parameterPolicyFactory.Create(parameterPart, parameterPart.ParameterPolicies[i]);
|
||||
if (parameterPolicy is IParameterTransformer parameterTransformer)
|
||||
{
|
||||
return parameterTransformer;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool AddQueryKeyValueToContext(UriBuildingContext context, string key, object value, bool wroteFirst)
|
||||
{
|
||||
var converted = Convert.ToString(value, CultureInfo.InvariantCulture);
|
||||
|
|
|
|||
|
|
@ -253,6 +253,35 @@ namespace Microsoft.AspNetCore.Routing
|
|||
Assert.Equal("/Foo/Bar%3Fencodeme%3F/Home/In%3Fdex?query=some%3Fquery#Fragment?", path);
|
||||
}
|
||||
|
||||
private class UpperCaseParameterTransform : IParameterTransformer
|
||||
{
|
||||
public string Transform(string value)
|
||||
{
|
||||
return value?.ToUpperInvariant();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLink_ParameterTransformer()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint = EndpointFactory.CreateRouteEndpoint("{controller:upper-case}/{name}");
|
||||
|
||||
var routeOptions = new RouteOptions();
|
||||
routeOptions.ConstraintMap["upper-case"] = typeof(UpperCaseParameterTransform);
|
||||
|
||||
var services = GetBasicServices();
|
||||
services.AddSingleton(typeof(UpperCaseParameterTransform), new UpperCaseParameterTransform());
|
||||
|
||||
var linkGenerator = CreateLinkGenerator(routeOptions, services, endpoint);
|
||||
|
||||
// Act
|
||||
var link = linkGenerator.GetPathByRouteValues(routeName: null, new { controller = "Home", name = "Test" });
|
||||
|
||||
// Assert
|
||||
Assert.Equal("/HOME/Test", link);
|
||||
}
|
||||
|
||||
// Includes characters that need to be encoded
|
||||
[Fact]
|
||||
public void GetPathByAddress_WithHttpContext_WithPathBaseAndFragment()
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
|||
var uriBuilldingContext = new UriBuildingContext(urlTestEncoder);
|
||||
|
||||
// Act
|
||||
uriBuilldingContext.EncodeValue(value, 0, value.Length, encodeSlashes: true);
|
||||
uriBuilldingContext.EncodeValue(value, 0, value.Length, encodeSlashes: true, parameterTransformer: null);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, uriBuilldingContext.ToString());
|
||||
|
|
@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
|||
var uriBuilldingContext = new UriBuildingContext(urlTestEncoder);
|
||||
|
||||
// Act
|
||||
uriBuilldingContext.EncodeValue(value, 0, value.Length, encodeSlashes: false);
|
||||
uriBuilldingContext.EncodeValue(value, 0, value.Length, encodeSlashes: false, parameterTransformer: null);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, uriBuilldingContext.ToString());
|
||||
|
|
@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
|||
var uriBuilldingContext = new UriBuildingContext(urlTestEncoder);
|
||||
|
||||
// Act
|
||||
uriBuilldingContext.EncodeValue(value, startIndex, characterCount, encodeSlashes: false);
|
||||
uriBuilldingContext.EncodeValue(value, startIndex, characterCount, encodeSlashes: false, parameterTransformer: null);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, uriBuilldingContext.ToString());
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
return httpContext;
|
||||
}
|
||||
|
||||
private ServiceCollection GetBasicServices()
|
||||
protected ServiceCollection GetBasicServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
|
||||
|
|
|
|||
|
|
@ -1582,7 +1582,6 @@ namespace Microsoft.AspNetCore.Routing
|
|||
// Assert
|
||||
var templateRoute = (Route)routeBuilder.Routes[0];
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedDictionary.Count, templateRoute.DataTokens.Count);
|
||||
foreach (var expectedKey in expectedDictionary.Keys)
|
||||
{
|
||||
|
|
@ -1591,6 +1590,22 @@ namespace Microsoft.AspNetCore.Routing
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RegisteringRoute_WithParameterPolicy_AbleToAddTheRoute()
|
||||
{
|
||||
// Arrange
|
||||
var routeBuilder = CreateRouteBuilder();
|
||||
|
||||
// Act
|
||||
routeBuilder.MapRoute("mockName",
|
||||
"{controller:test-policy}/{action}");
|
||||
|
||||
// Assert
|
||||
var templateRoute = (Route)routeBuilder.Routes[0];
|
||||
|
||||
Assert.Empty(templateRoute.Constraints);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RegisteringRouteWithInvalidConstraints_Throws()
|
||||
{
|
||||
|
|
@ -1752,6 +1767,8 @@ namespace Microsoft.AspNetCore.Routing
|
|||
var services = new ServiceCollection();
|
||||
services.AddSingleton<IInlineConstraintResolver>(_inlineConstraintResolver);
|
||||
services.AddSingleton<RoutingMarkerService>();
|
||||
services.AddSingleton<ParameterPolicyFactory, DefaultParameterPolicyFactory>();
|
||||
services.Configure<RouteOptions>(ConfigureRouteOptions);
|
||||
|
||||
var applicationBuilder = Mock.Of<IApplicationBuilder>();
|
||||
applicationBuilder.ApplicationServices = services.BuildServiceProvider();
|
||||
|
|
@ -1837,12 +1854,24 @@ namespace Microsoft.AspNetCore.Routing
|
|||
|
||||
private static IInlineConstraintResolver GetInlineConstraintResolver()
|
||||
{
|
||||
var routeOptions = new Mock<IOptions<RouteOptions>>();
|
||||
routeOptions
|
||||
.SetupGet(o => o.Value)
|
||||
.Returns(new RouteOptions());
|
||||
var routeOptions = new RouteOptions();
|
||||
ConfigureRouteOptions(routeOptions);
|
||||
|
||||
return new DefaultInlineConstraintResolver(routeOptions.Object, new TestServiceProvider());
|
||||
var routeOptionsMock = new Mock<IOptions<RouteOptions>>();
|
||||
routeOptionsMock
|
||||
.SetupGet(o => o.Value)
|
||||
.Returns(routeOptions);
|
||||
|
||||
return new DefaultInlineConstraintResolver(routeOptionsMock.Object, new TestServiceProvider());
|
||||
}
|
||||
|
||||
private static void ConfigureRouteOptions(RouteOptions options)
|
||||
{
|
||||
options.ConstraintMap["test-policy"] = typeof(TestPolicy);
|
||||
}
|
||||
|
||||
private class TestPolicy : IParameterPolicy
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Text.Encodings.Web;
|
||||
using Microsoft.AspNetCore.Routing.Internal;
|
||||
using Microsoft.AspNetCore.Routing.Patterns;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
|
@ -1288,6 +1289,43 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
|
|||
Assert.Equal(expected, boundTemplate);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BindValues_ParameterTransformer()
|
||||
{
|
||||
// Arrange
|
||||
var routeOptions = new RouteOptions();
|
||||
routeOptions.ConstraintMap["test-transformer"] = typeof(TestParameterTransformer);
|
||||
var parameterPolicyFactory = new DefaultParameterPolicyFactory(
|
||||
Options.Create(routeOptions),
|
||||
new ServiceCollection().BuildServiceProvider());
|
||||
var expected = "/ConventionalTansformerRoute/_ConventionalTansformer_/Param/_value_";
|
||||
var template = "ConventionalTansformerRoute/_ConventionalTansformer_/Param/{param:length(500):test-transformer?}";
|
||||
var defaults = new RouteValueDictionary(new { controller = "ConventionalTansformer", action = "Param" });
|
||||
var ambientValues = new RouteValueDictionary(new { controller = "ConventionalTansformer", action = "Param" });
|
||||
var explicitValues = new RouteValueDictionary(new { controller = "ConventionalTansformer", action = "Param", param = "value" });
|
||||
var binder = new TemplateBinder(
|
||||
UrlEncoder.Default,
|
||||
new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy()),
|
||||
RoutePatternFactory.Parse(template),
|
||||
defaults,
|
||||
parameterPolicyFactory);
|
||||
|
||||
// Act
|
||||
var result = binder.GetValues(ambientValues, explicitValues);
|
||||
var boundTemplate = binder.BindValues(result.AcceptedValues);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, boundTemplate);
|
||||
}
|
||||
|
||||
private class TestParameterTransformer : IParameterTransformer
|
||||
{
|
||||
public string Transform(string value)
|
||||
{
|
||||
return "_" + value + "_";
|
||||
}
|
||||
}
|
||||
|
||||
private static IInlineConstraintResolver GetInlineConstraintResolver()
|
||||
{
|
||||
var services = new ServiceCollection().AddOptions();
|
||||
|
|
|
|||
|
|
@ -132,6 +132,8 @@ namespace Microsoft.AspNetCore.Routing.Tests
|
|||
var services = new ServiceCollection();
|
||||
services.AddSingleton<IInlineConstraintResolver>(_inlineConstraintResolver);
|
||||
services.AddSingleton<RoutingMarkerService>();
|
||||
services.AddSingleton<ParameterPolicyFactory, DefaultParameterPolicyFactory>();
|
||||
services.Configure<RouteOptions>(options => { });
|
||||
|
||||
var applicationBuilder = Mock.Of<IApplicationBuilder>();
|
||||
applicationBuilder.ApplicationServices = services.BuildServiceProvider();
|
||||
|
|
|
|||
Loading…
Reference in New Issue