Transform parameters before lower casing in link generation (#793)

This commit is contained in:
James Newton-King 2018-09-14 17:19:43 +12:00 committed by GitHub
parent 426a48a65a
commit 412944e1f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 83 additions and 30 deletions

View File

@ -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)
{

View File

@ -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()
{

View File

@ -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());

View File

@ -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();

View File

@ -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();
}
}
}