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
|
namespace Microsoft.AspNetCore.Routing.Constraints
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constrains a route parameter to contain only a specified strign.
|
/// Constrains a route parameter to contain only a specified string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class StringRouteConstraint : IRouteConstraint
|
public class StringRouteConstraint : IRouteConstraint
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,12 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
throw new ArgumentNullException(nameof(inlineConstraint));
|
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,
|
UrlEncoder.Default,
|
||||||
_uriBuildingContextPool,
|
_uriBuildingContextPool,
|
||||||
endpoint.RoutePattern,
|
endpoint.RoutePattern,
|
||||||
new RouteValueDictionary(endpoint.RoutePattern.Defaults));
|
new RouteValueDictionary(endpoint.RoutePattern.Defaults),
|
||||||
|
_parameterPolicyFactory);
|
||||||
|
|
||||||
var routeValuesAddressMetadata = endpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
|
var routeValuesAddressMetadata = endpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
|
||||||
var templateValuesResult = templateBinder.GetValues(
|
var templateValuesResult = templateBinder.GetValues(
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,12 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
throw new ArgumentNullException(nameof(inlineText));
|
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)
|
if (parameterPolicy == null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(Resources.FormatRoutePattern_ConstraintReferenceNotFound(
|
throw new InvalidOperationException(Resources.FormatRoutePattern_ConstraintReferenceNotFound(
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,11 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
||||||
{
|
{
|
||||||
internal static class ParameterPolicyActivator
|
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
|
where T : IParameterPolicy
|
||||||
{
|
{
|
||||||
// IServiceProvider could be null
|
// IServiceProvider could be null
|
||||||
|
|
@ -51,9 +55,18 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
||||||
|
|
||||||
if (!typeof(T).IsAssignableFrom(parameterPolicyType))
|
if (!typeof(T).IsAssignableFrom(parameterPolicyType))
|
||||||
{
|
{
|
||||||
throw new RouteCreationException(
|
if (!typeof(IParameterPolicy).IsAssignableFrom(parameterPolicyType))
|
||||||
Resources.FormatDefaultInlineConstraintResolver_TypeNotConstraint(
|
{
|
||||||
parameterPolicyType, parameterPolicyKey, typeof(T).Name));
|
// 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
|
try
|
||||||
|
|
|
||||||
|
|
@ -56,10 +56,10 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
||||||
|
|
||||||
public bool Accept(string value)
|
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))
|
if (string.IsNullOrEmpty(value))
|
||||||
{
|
{
|
||||||
|
|
@ -117,11 +117,11 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
||||||
if (_path.Length == 0 && value.Length > 0 && value[0] == '/')
|
if (_path.Length == 0 && value.Length > 0 && value[0] == '/')
|
||||||
{
|
{
|
||||||
_path.Append("/");
|
_path.Append("/");
|
||||||
EncodeValue(value, 1, value.Length - 1, encodeSlashes);
|
EncodeValue(value, 1, value.Length - 1, encodeSlashes, parameterTransformer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EncodeValue(value, encodeSlashes);
|
EncodeValue(value, encodeSlashes, parameterTransformer);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -263,17 +263,24 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
||||||
|
|
||||||
private void EncodeValue(string value)
|
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
|
// 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
|
// Just encode everything if its ok to encode slashes
|
||||||
if (encodeSlashes)
|
if (encodeSlashes)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Microsoft.AspNetCore.Routing.Constraints;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Builder
|
namespace Microsoft.AspNetCore.Builder
|
||||||
|
|
@ -107,10 +108,6 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
throw new RouteCreationException(Resources.FormatDefaultHandler_MustBeSet(nameof(IRouteBuilder)));
|
throw new RouteCreationException(Resources.FormatDefaultHandler_MustBeSet(nameof(IRouteBuilder)));
|
||||||
}
|
}
|
||||||
|
|
||||||
var inlineConstraintResolver = routeBuilder
|
|
||||||
.ServiceProvider
|
|
||||||
.GetRequiredService<IInlineConstraintResolver>();
|
|
||||||
|
|
||||||
routeBuilder.Routes.Add(new Route(
|
routeBuilder.Routes.Add(new Route(
|
||||||
routeBuilder.DefaultHandler,
|
routeBuilder.DefaultHandler,
|
||||||
name,
|
name,
|
||||||
|
|
@ -118,9 +115,52 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
new RouteValueDictionary(defaults),
|
new RouteValueDictionary(defaults),
|
||||||
new RouteValueDictionary(constraints),
|
new RouteValueDictionary(constraints),
|
||||||
new RouteValueDictionary(dataTokens),
|
new RouteValueDictionary(dataTokens),
|
||||||
inlineConstraintResolver));
|
CreateInlineConstraintResolver(routeBuilder.ServiceProvider)));
|
||||||
|
|
||||||
return routeBuilder;
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Routing.Constraints;
|
using Microsoft.AspNetCore.Routing.Constraints;
|
||||||
|
using Microsoft.AspNetCore.Routing.Internal;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Routing
|
namespace Microsoft.AspNetCore.Routing
|
||||||
{
|
{
|
||||||
|
|
@ -158,6 +159,11 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
_displayName,
|
_displayName,
|
||||||
_inlineConstraintResolver.GetType().Name));
|
_inlineConstraintResolver.GetType().Name));
|
||||||
}
|
}
|
||||||
|
else if (constraint == NullRouteConstraint.Instance)
|
||||||
|
{
|
||||||
|
// A null route constraint can be returned for other parameter policy types
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Add(key, constraint);
|
Add(key, constraint);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ namespace Microsoft.AspNetCore.Routing.Template
|
||||||
private readonly ObjectPool<UriBuildingContext> _pool;
|
private readonly ObjectPool<UriBuildingContext> _pool;
|
||||||
|
|
||||||
private readonly RouteValueDictionary _defaults;
|
private readonly RouteValueDictionary _defaults;
|
||||||
|
private readonly ParameterPolicyFactory _parameterPolicyFactory;
|
||||||
private readonly RouteValueDictionary _filters;
|
private readonly RouteValueDictionary _filters;
|
||||||
private readonly RoutePattern _pattern;
|
private readonly RoutePattern _pattern;
|
||||||
|
|
||||||
|
|
@ -35,7 +36,7 @@ namespace Microsoft.AspNetCore.Routing.Template
|
||||||
ObjectPool<UriBuildingContext> pool,
|
ObjectPool<UriBuildingContext> pool,
|
||||||
RouteTemplate template,
|
RouteTemplate template,
|
||||||
RouteValueDictionary defaults)
|
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="pool">The <see cref="ObjectPool{T}"/>.</param>
|
||||||
/// <param name="pattern">The <see cref="RoutePattern"/> to bind values to.</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="defaults">The default values for <paramref name="pattern"/>.</param>
|
||||||
|
/// <param name="parameterPolicyFactory">The <see cref="ParameterPolicyFactory"/>.</param>
|
||||||
public TemplateBinder(
|
public TemplateBinder(
|
||||||
UrlEncoder urlEncoder,
|
UrlEncoder urlEncoder,
|
||||||
ObjectPool<UriBuildingContext> pool,
|
ObjectPool<UriBuildingContext> pool,
|
||||||
RoutePattern pattern,
|
RoutePattern pattern,
|
||||||
RouteValueDictionary defaults)
|
RouteValueDictionary defaults,
|
||||||
|
ParameterPolicyFactory parameterPolicyFactory)
|
||||||
{
|
{
|
||||||
if (urlEncoder == null)
|
if (urlEncoder == null)
|
||||||
{
|
{
|
||||||
|
|
@ -71,6 +74,7 @@ namespace Microsoft.AspNetCore.Routing.Template
|
||||||
_pool = pool;
|
_pool = pool;
|
||||||
_pattern = pattern;
|
_pattern = pattern;
|
||||||
_defaults = defaults;
|
_defaults = defaults;
|
||||||
|
_parameterPolicyFactory = parameterPolicyFactory;
|
||||||
|
|
||||||
// Any default that doesn't have a corresponding parameter is a 'filter' and if a value
|
// 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.
|
// 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
|
// Example: template = {id}.{format?}. parameters: id=5
|
||||||
// In this case after we have generated "5.", we wont find any value
|
// In this case after we have generated "5.", we wont find any value
|
||||||
// for format, so we remove '.' and generate 5.
|
// 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)
|
if (j != 0 && parameterPart.IsOptional && (separatorPart = segment.Parts[j - 1] as RoutePatternSeparatorPart) != null)
|
||||||
{
|
{
|
||||||
|
|
@ -310,6 +314,26 @@ namespace Microsoft.AspNetCore.Routing.Template
|
||||||
return true;
|
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)
|
private bool AddQueryKeyValueToContext(UriBuildingContext context, string key, object value, bool wroteFirst)
|
||||||
{
|
{
|
||||||
var converted = Convert.ToString(value, CultureInfo.InvariantCulture);
|
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);
|
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
|
// Includes characters that need to be encoded
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetPathByAddress_WithHttpContext_WithPathBaseAndFragment()
|
public void GetPathByAddress_WithHttpContext_WithPathBaseAndFragment()
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
||||||
var uriBuilldingContext = new UriBuildingContext(urlTestEncoder);
|
var uriBuilldingContext = new UriBuildingContext(urlTestEncoder);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
uriBuilldingContext.EncodeValue(value, 0, value.Length, encodeSlashes: true);
|
uriBuilldingContext.EncodeValue(value, 0, value.Length, encodeSlashes: true, parameterTransformer: null);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(expected, uriBuilldingContext.ToString());
|
Assert.Equal(expected, uriBuilldingContext.ToString());
|
||||||
|
|
@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
||||||
var uriBuilldingContext = new UriBuildingContext(urlTestEncoder);
|
var uriBuilldingContext = new UriBuildingContext(urlTestEncoder);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
uriBuilldingContext.EncodeValue(value, 0, value.Length, encodeSlashes: false);
|
uriBuilldingContext.EncodeValue(value, 0, value.Length, encodeSlashes: false, parameterTransformer: null);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(expected, uriBuilldingContext.ToString());
|
Assert.Equal(expected, uriBuilldingContext.ToString());
|
||||||
|
|
@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
||||||
var uriBuilldingContext = new UriBuildingContext(urlTestEncoder);
|
var uriBuilldingContext = new UriBuildingContext(urlTestEncoder);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
uriBuilldingContext.EncodeValue(value, startIndex, characterCount, encodeSlashes: false);
|
uriBuilldingContext.EncodeValue(value, startIndex, characterCount, encodeSlashes: false, parameterTransformer: null);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(expected, uriBuilldingContext.ToString());
|
Assert.Equal(expected, uriBuilldingContext.ToString());
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
return httpContext;
|
return httpContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ServiceCollection GetBasicServices()
|
protected ServiceCollection GetBasicServices()
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
|
services.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
|
||||||
|
|
|
||||||
|
|
@ -1582,7 +1582,6 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
// Assert
|
// Assert
|
||||||
var templateRoute = (Route)routeBuilder.Routes[0];
|
var templateRoute = (Route)routeBuilder.Routes[0];
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(expectedDictionary.Count, templateRoute.DataTokens.Count);
|
Assert.Equal(expectedDictionary.Count, templateRoute.DataTokens.Count);
|
||||||
foreach (var expectedKey in expectedDictionary.Keys)
|
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]
|
[Fact]
|
||||||
public void RegisteringRouteWithInvalidConstraints_Throws()
|
public void RegisteringRouteWithInvalidConstraints_Throws()
|
||||||
{
|
{
|
||||||
|
|
@ -1752,6 +1767,8 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddSingleton<IInlineConstraintResolver>(_inlineConstraintResolver);
|
services.AddSingleton<IInlineConstraintResolver>(_inlineConstraintResolver);
|
||||||
services.AddSingleton<RoutingMarkerService>();
|
services.AddSingleton<RoutingMarkerService>();
|
||||||
|
services.AddSingleton<ParameterPolicyFactory, DefaultParameterPolicyFactory>();
|
||||||
|
services.Configure<RouteOptions>(ConfigureRouteOptions);
|
||||||
|
|
||||||
var applicationBuilder = Mock.Of<IApplicationBuilder>();
|
var applicationBuilder = Mock.Of<IApplicationBuilder>();
|
||||||
applicationBuilder.ApplicationServices = services.BuildServiceProvider();
|
applicationBuilder.ApplicationServices = services.BuildServiceProvider();
|
||||||
|
|
@ -1837,12 +1854,24 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
|
|
||||||
private static IInlineConstraintResolver GetInlineConstraintResolver()
|
private static IInlineConstraintResolver GetInlineConstraintResolver()
|
||||||
{
|
{
|
||||||
var routeOptions = new Mock<IOptions<RouteOptions>>();
|
var routeOptions = new RouteOptions();
|
||||||
routeOptions
|
ConfigureRouteOptions(routeOptions);
|
||||||
.SetupGet(o => o.Value)
|
|
||||||
.Returns(new 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.Linq;
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
using Microsoft.AspNetCore.Routing.Internal;
|
using Microsoft.AspNetCore.Routing.Internal;
|
||||||
|
using Microsoft.AspNetCore.Routing.Patterns;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.ObjectPool;
|
using Microsoft.Extensions.ObjectPool;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
@ -1288,6 +1289,43 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
|
||||||
Assert.Equal(expected, boundTemplate);
|
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()
|
private static IInlineConstraintResolver GetInlineConstraintResolver()
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection().AddOptions();
|
var services = new ServiceCollection().AddOptions();
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,8 @@ namespace Microsoft.AspNetCore.Routing.Tests
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddSingleton<IInlineConstraintResolver>(_inlineConstraintResolver);
|
services.AddSingleton<IInlineConstraintResolver>(_inlineConstraintResolver);
|
||||||
services.AddSingleton<RoutingMarkerService>();
|
services.AddSingleton<RoutingMarkerService>();
|
||||||
|
services.AddSingleton<ParameterPolicyFactory, DefaultParameterPolicyFactory>();
|
||||||
|
services.Configure<RouteOptions>(options => { });
|
||||||
|
|
||||||
var applicationBuilder = Mock.Of<IApplicationBuilder>();
|
var applicationBuilder = Mock.Of<IApplicationBuilder>();
|
||||||
applicationBuilder.ApplicationServices = services.BuildServiceProvider();
|
applicationBuilder.ApplicationServices = services.BuildServiceProvider();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue