Transform parameters before lower casing in link generation (#793)
This commit is contained in:
parent
426a48a65a
commit
412944e1f5
|
|
@ -80,6 +80,13 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
|||
return false;
|
||||
}
|
||||
|
||||
// NOTE: call the parameter transformer before changing the case
|
||||
// A transformer might use the case, e.g. AllProducts -> all-products
|
||||
if (parameterTransformer != null)
|
||||
{
|
||||
value = parameterTransformer.Transform(value);
|
||||
}
|
||||
|
||||
// NOTE: this needs to be above all 'EncodeValue' and _path.Append calls
|
||||
if (LowercaseUrls)
|
||||
{
|
||||
|
|
@ -117,11 +124,11 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
|||
if (_path.Length == 0 && value.Length > 0 && value[0] == '/')
|
||||
{
|
||||
_path.Append("/");
|
||||
EncodeValue(value, 1, value.Length - 1, encodeSlashes, parameterTransformer);
|
||||
EncodeValue(value, 1, value.Length - 1, encodeSlashes);
|
||||
}
|
||||
else
|
||||
{
|
||||
EncodeValue(value, encodeSlashes, parameterTransformer);
|
||||
EncodeValue(value, encodeSlashes);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -263,24 +270,17 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
|||
|
||||
private void EncodeValue(string value)
|
||||
{
|
||||
EncodeValue(value, encodeSlashes: true, parameterTransformer: null);
|
||||
EncodeValue(value, encodeSlashes: true);
|
||||
}
|
||||
|
||||
private void EncodeValue(string value, bool encodeSlashes, IParameterTransformer parameterTransformer)
|
||||
private void EncodeValue(string value, bool encodeSlashes)
|
||||
{
|
||||
EncodeValue(value, start: 0, characterCount: value.Length, encodeSlashes, parameterTransformer);
|
||||
EncodeValue(value, start: 0, characterCount: value.Length, encodeSlashes);
|
||||
}
|
||||
|
||||
// For testing
|
||||
internal void EncodeValue(string value, int start, int characterCount, bool encodeSlashes, IParameterTransformer parameterTransformer)
|
||||
internal void EncodeValue(string value, int start, int characterCount, bool encodeSlashes)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -169,6 +169,49 @@ namespace Microsoft.AspNetCore.Routing
|
|||
Assert.Equal("/Home/Index/", path);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPathByAddress_WithParameterTransformer()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller:slugify}/{action}/{id}", metadata: new object[] { new IntMetadata(1), });
|
||||
var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller:slugify}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), });
|
||||
|
||||
var routeOptions = new RouteOptions();
|
||||
routeOptions.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer);
|
||||
|
||||
var linkGenerator = CreateLinkGenerator(routeOptions: routeOptions, services: null, endpoint1, endpoint2);
|
||||
|
||||
// Act
|
||||
var path = linkGenerator.GetPathByAddress(
|
||||
1,
|
||||
values: new RouteValueDictionary(new { controller = "TestController", action = "Index", }));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("/test-controller/Index", path);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPathByAddress_WithParameterTransformer_WithLowercaseUrl()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller:slugify}/{action}/{id}", metadata: new object[] { new IntMetadata(1), });
|
||||
var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller:slugify}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), });
|
||||
|
||||
var routeOptions = new RouteOptions();
|
||||
routeOptions.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer);
|
||||
|
||||
var linkGenerator = CreateLinkGenerator(routeOptions: routeOptions, services: null, endpoint1, endpoint2);
|
||||
|
||||
// Act
|
||||
var path = linkGenerator.GetPathByAddress(
|
||||
1,
|
||||
values: new RouteValueDictionary(new { controller = "TestController", action = "Index", }),
|
||||
options: new LinkOptions() { LowercaseUrls = true, });
|
||||
|
||||
// Assert
|
||||
Assert.Equal("/test-controller/index", path);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPathByAddress_WithHttpContext_WithLinkOptions()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
|||
var uriBuilldingContext = new UriBuildingContext(urlTestEncoder);
|
||||
|
||||
// Act
|
||||
uriBuilldingContext.EncodeValue(value, 0, value.Length, encodeSlashes: true, parameterTransformer: null);
|
||||
uriBuilldingContext.EncodeValue(value, 0, value.Length, encodeSlashes: true);
|
||||
|
||||
// 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, parameterTransformer: null);
|
||||
uriBuilldingContext.EncodeValue(value, 0, value.Length, encodeSlashes: false);
|
||||
|
||||
// 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, parameterTransformer: null);
|
||||
uriBuilldingContext.EncodeValue(value, startIndex, characterCount, encodeSlashes: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, uriBuilldingContext.ToString());
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.Linq;
|
|||
using System.Text.Encodings.Web;
|
||||
using Microsoft.AspNetCore.Routing.Internal;
|
||||
using Microsoft.AspNetCore.Routing.Patterns;
|
||||
using Microsoft.AspNetCore.Routing.TestObjects;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
|
@ -1294,15 +1295,15 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
|
|||
{
|
||||
// Arrange
|
||||
var routeOptions = new RouteOptions();
|
||||
routeOptions.ConstraintMap["test-transformer"] = typeof(TestParameterTransformer);
|
||||
routeOptions.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer);
|
||||
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 expected = "/ConventionalTransformerRoute/conventional-transformer/Param/my-value";
|
||||
var template = "ConventionalTransformerRoute/conventional-transformer/Param/{param:length(500):slugify?}";
|
||||
var defaults = new RouteValueDictionary(new { controller = "ConventionalTransformer", action = "Param" });
|
||||
var ambientValues = new RouteValueDictionary(new { controller = "ConventionalTransformer", action = "Param" });
|
||||
var explicitValues = new RouteValueDictionary(new { controller = "ConventionalTransformer", action = "Param", param = "MyValue" });
|
||||
var binder = new TemplateBinder(
|
||||
UrlEncoder.Default,
|
||||
new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy()),
|
||||
|
|
@ -1319,14 +1320,6 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
|
|||
Assert.Equal(expected, boundTemplate);
|
||||
}
|
||||
|
||||
private class TestParameterTransformer : IParameterTransformer
|
||||
{
|
||||
public string Transform(string value)
|
||||
{
|
||||
return "_" + value + "_";
|
||||
}
|
||||
}
|
||||
|
||||
private static IInlineConstraintResolver GetInlineConstraintResolver()
|
||||
{
|
||||
var services = new ServiceCollection().AddOptions();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
// 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.Text.RegularExpressions;
|
||||
|
||||
namespace Microsoft.AspNetCore.Routing.TestObjects
|
||||
{
|
||||
public class SlugifyParameterTransformer : IParameterTransformer
|
||||
{
|
||||
public string Transform(string value)
|
||||
{
|
||||
// Slugify value
|
||||
return Regex.Replace(value, "([a-z])([A-Z])", "$1-$2").ToLower();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue