Updated to make routing always use UrlEncoder.Default and not depend on DI to get it.

[Fixes #513] RedirectToAction with Non-English Characters in Parameters and Authentication Causes Error
This commit is contained in:
Kiran Challa 2018-04-10 05:07:39 -07:00
parent 8f62b6e5ba
commit 4184b2406d
10 changed files with 232 additions and 118 deletions

View File

@ -7,15 +7,12 @@ using System.Text.Encodings.Web;
using System.Threading.Tasks; using System.Threading.Tasks;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Internal; using Microsoft.AspNetCore.Routing.Internal;
using Microsoft.AspNetCore.Routing.Template; using Microsoft.AspNetCore.Routing.Template;
using Microsoft.AspNetCore.Routing.Tree; using Microsoft.AspNetCore.Routing.Tree;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System.Diagnostics;
namespace Microsoft.AspNetCore.Routing.Performance namespace Microsoft.AspNetCore.Routing.Performance
{ {
@ -33,8 +30,7 @@ namespace Microsoft.AspNetCore.Routing.Performance
var treeBuilder = new TreeRouteBuilder( var treeBuilder = new TreeRouteBuilder(
NullLoggerFactory.Instance, NullLoggerFactory.Instance,
UrlEncoder.Default, new DefaultObjectPool<UriBuildingContext>(new UriBuilderContextPooledObjectPolicy()),
new DefaultObjectPool<UriBuildingContext>(new UriBuilderContextPooledObjectPolicy(UrlEncoder.Default)),
new DefaultInlineConstraintResolver(new OptionsManager<RouteOptions>(new OptionsFactory<RouteOptions>(Enumerable.Empty<IConfigureOptions<RouteOptions>>(), Enumerable.Empty<IPostConfigureOptions<RouteOptions>>())))); new DefaultInlineConstraintResolver(new OptionsManager<RouteOptions>(new OptionsFactory<RouteOptions>(Enumerable.Empty<IConfigureOptions<RouteOptions>>(), Enumerable.Empty<IPostConfigureOptions<RouteOptions>>()))));
treeBuilder.MapInbound(handler, TemplateParser.Parse("api/Widgets"), "default", 0); treeBuilder.MapInbound(handler, TemplateParser.Parse("api/Widgets"), "default", 0);

View File

@ -2,11 +2,11 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Internal; using Microsoft.AspNetCore.Routing.Internal;
using Microsoft.AspNetCore.Routing.Tree; using Microsoft.AspNetCore.Routing.Tree;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.ObjectPool;
namespace Microsoft.Extensions.DependencyInjection namespace Microsoft.Extensions.DependencyInjection
@ -29,17 +29,21 @@ namespace Microsoft.Extensions.DependencyInjection
} }
services.TryAddTransient<IInlineConstraintResolver, DefaultInlineConstraintResolver>(); services.TryAddTransient<IInlineConstraintResolver, DefaultInlineConstraintResolver>();
services.TryAddSingleton(UrlEncoder.Default);
services.TryAddSingleton<ObjectPool<UriBuildingContext>>(s => services.TryAddSingleton<ObjectPool<UriBuildingContext>>(s =>
{ {
var provider = s.GetRequiredService<ObjectPoolProvider>(); var provider = s.GetRequiredService<ObjectPoolProvider>();
var encoder = s.GetRequiredService<UrlEncoder>(); return provider.Create<UriBuildingContext>(new UriBuilderContextPooledObjectPolicy());
return provider.Create<UriBuildingContext>(new UriBuilderContextPooledObjectPolicy(encoder));
}); });
// The TreeRouteBuilder is a builder for creating routes, it should stay transient because it's // The TreeRouteBuilder is a builder for creating routes, it should stay transient because it's
// stateful. // stateful.
services.TryAddTransient<TreeRouteBuilder>(); services.TryAdd(ServiceDescriptor.Transient<TreeRouteBuilder>(s =>
{
var loggerFactory = s.GetRequiredService<ILoggerFactory>();
var objectPool = s.GetRequiredService<ObjectPool<UriBuildingContext>>();
var constraintResolver = s.GetRequiredService<IInlineConstraintResolver>();
return new TreeRouteBuilder(loggerFactory, objectPool, constraintResolver);
}));
services.TryAddSingleton(typeof(RoutingMarkerService)); services.TryAddSingleton(typeof(RoutingMarkerService));

View File

@ -1,7 +1,6 @@
// 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.ObjectPool;
@ -9,21 +8,9 @@ namespace Microsoft.AspNetCore.Routing.Internal
{ {
public class UriBuilderContextPooledObjectPolicy : IPooledObjectPolicy<UriBuildingContext> public class UriBuilderContextPooledObjectPolicy : IPooledObjectPolicy<UriBuildingContext>
{ {
private readonly UrlEncoder _encoder;
public UriBuilderContextPooledObjectPolicy(UrlEncoder encoder)
{
if (encoder == null)
{
throw new ArgumentNullException(nameof(encoder));
}
_encoder = encoder;
}
public UriBuildingContext Create() public UriBuildingContext Create()
{ {
return new UriBuildingContext(_encoder); return new UriBuildingContext(UrlEncoder.Default);
} }
public bool Return(UriBuildingContext obj) public bool Return(UriBuildingContext obj)

View File

@ -241,9 +241,8 @@ namespace Microsoft.AspNetCore.Routing
{ {
if (_binder == null) if (_binder == null)
{ {
var urlEncoder = context.RequestServices.GetRequiredService<UrlEncoder>();
var pool = context.RequestServices.GetRequiredService<ObjectPool<UriBuildingContext>>(); var pool = context.RequestServices.GetRequiredService<ObjectPool<UriBuildingContext>>();
_binder = new TemplateBinder(urlEncoder, pool, ParsedTemplate, Defaults); _binder = new TemplateBinder(UrlEncoder.Default, pool, ParsedTemplate, Defaults);
} }
} }

View File

@ -20,6 +20,13 @@ namespace Microsoft.AspNetCore.Routing.Template
private readonly RouteValueDictionary _filters; private readonly RouteValueDictionary _filters;
private readonly RouteTemplate _template; private readonly RouteTemplate _template;
/// <summary>
/// Creates a new instance of <see cref="TemplateBinder"/>.
/// </summary>
/// <param name="urlEncoder">The <see cref="UrlEncoder"/>.</param>
/// <param name="pool">The <see cref="ObjectPool{T}"/>.</param>
/// <param name="template">The <see cref="RouteTemplate"/> to bind values to.</param>
/// <param name="defaults">The default values for <paramref name="template"/>.</param>
public TemplateBinder( public TemplateBinder(
UrlEncoder urlEncoder, UrlEncoder urlEncoder,
ObjectPool<UriBuildingContext> pool, ObjectPool<UriBuildingContext> pool,
@ -90,8 +97,8 @@ namespace Microsoft.AspNetCore.Routing.Template
} }
} }
if (!hasNewParameterValue && if (!hasNewParameterValue &&
!hasCurrentParameterValue && !hasCurrentParameterValue &&
_defaults?.ContainsKey(parameter.Name) != true) _defaults?.ContainsKey(parameter.Name) != true)
{ {
// This is an unsatisfied parameter value and there are no defaults. We might still // This is an unsatisfied parameter value and there are no defaults. We might still

View File

@ -25,28 +25,49 @@ namespace Microsoft.AspNetCore.Routing.Tree
private readonly IInlineConstraintResolver _constraintResolver; private readonly IInlineConstraintResolver _constraintResolver;
/// <summary> /// <summary>
/// Initializes a new instance of <see cref="TreeRouteBuilder"/>. /// <para>
/// This constructor is obsolete and will be removed in a future version. The recommended
/// alternative is the overload that does not take a UrlEncoder.
/// </para>
/// <para>Initializes a new instance of <see cref="TreeRouteBuilder"/>.</para>
/// </summary> /// </summary>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param> /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
/// <param name="urlEncoder">The <see cref="UrlEncoder"/>.</param> /// <param name="urlEncoder">The <see cref="UrlEncoder"/>.</param>
/// <param name="objectPool">The <see cref="ObjectPool{UrlBuildingContext}"/>.</param> /// <param name="objectPool">The <see cref="ObjectPool{UrlBuildingContext}"/>.</param>
/// <param name="constraintResolver">The <see cref="IInlineConstraintResolver"/>.</param> /// <param name="constraintResolver">The <see cref="IInlineConstraintResolver"/>.</param>
[Obsolete("This constructor is obsolete and will be removed in a future version. The recommended " +
"alternative is the overload that does not take a UrlEncoder.")]
public TreeRouteBuilder( public TreeRouteBuilder(
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
UrlEncoder urlEncoder, UrlEncoder urlEncoder,
ObjectPool<UriBuildingContext> objectPool, ObjectPool<UriBuildingContext> objectPool,
IInlineConstraintResolver constraintResolver) IInlineConstraintResolver constraintResolver)
: this(loggerFactory, objectPool, constraintResolver)
{
if (urlEncoder == null)
{
throw new ArgumentNullException(nameof(urlEncoder));
}
_urlEncoder = urlEncoder;
}
/// <summary>
/// Initializes a new instance of <see cref="TreeRouteBuilder"/>.
/// </summary>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
/// <param name="objectPool">The <see cref="ObjectPool{UrlBuildingContext}"/>.</param>
/// <param name="constraintResolver">The <see cref="IInlineConstraintResolver"/>.</param>
public TreeRouteBuilder(
ILoggerFactory loggerFactory,
ObjectPool<UriBuildingContext> objectPool,
IInlineConstraintResolver constraintResolver)
{ {
if (loggerFactory == null) if (loggerFactory == null)
{ {
throw new ArgumentNullException(nameof(loggerFactory)); throw new ArgumentNullException(nameof(loggerFactory));
} }
if (urlEncoder == null)
{
throw new ArgumentNullException(nameof(urlEncoder));
}
if (objectPool == null) if (objectPool == null)
{ {
throw new ArgumentNullException(nameof(objectPool)); throw new ArgumentNullException(nameof(objectPool));
@ -57,7 +78,7 @@ namespace Microsoft.AspNetCore.Routing.Tree
throw new ArgumentNullException(nameof(constraintResolver)); throw new ArgumentNullException(nameof(constraintResolver));
} }
_urlEncoder = urlEncoder; _urlEncoder = UrlEncoder.Default;
_objectPool = objectPool; _objectPool = objectPool;
_constraintResolver = constraintResolver; _constraintResolver = constraintResolver;

View File

@ -3,6 +3,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.Encodings.Web;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -14,6 +15,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.Extensions.WebEncoders.Testing;
using Moq; using Moq;
using Xunit; using Xunit;
@ -647,6 +649,37 @@ namespace Microsoft.AspNetCore.Routing
Assert.Empty(pathData.DataTokens); Assert.Empty(pathData.DataTokens);
} }
[Fact]
public void GetVirtualPath_AlwaysUsesDefaultUrlEncoder()
{
// Arrange
var services = new ServiceCollection();
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
services.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
services.AddRouting();
// This test encoder should not be used by Routing and should always use the default one.
services.AddSingleton<UrlEncoder>(new UrlTestEncoder());
var httpContext = new DefaultHttpContext
{
RequestServices = services.BuildServiceProvider(),
};
var context = new VirtualPathContext(
httpContext,
values: new RouteValueDictionary(new { name = "name with %special #characters Jörn" }),
ambientValues: new RouteValueDictionary(new { controller = "Home", action = "Index" }));
var route = CreateRoute("{controller}/{action}");
// Act
var pathData = route.GetVirtualPath(context);
// Assert
Assert.Equal("/Home/Index?name=name%20with%20%25special%20%23characters%20J%C3%B6rn", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
[Fact] [Fact]
public void GetVirtualPath_ForListOfStrings() public void GetVirtualPath_ForListOfStrings()
{ {

View File

@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Routing.Internal;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.Extensions.WebEncoders.Testing;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Routing.Template.Tests namespace Microsoft.AspNetCore.Routing.Template.Tests
@ -31,7 +30,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
"Test/{val1}/{val2}", "Test/{val1}/{val2}",
new RouteValueDictionary(new {val1 = "", val2 = ""}), new RouteValueDictionary(new {val1 = "", val2 = ""}),
new RouteValueDictionary(new {val1 = "a"}), new RouteValueDictionary(new {val1 = "a"}),
"/UrlEncode[[Test]]/UrlEncode[[a]]" "/Test/a"
}, },
{ {
"Test/{val1}/{val2}/{val3}", "Test/{val1}/{val2}/{val3}",
@ -43,67 +42,67 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
"Test/{val1}/{val2}", "Test/{val1}/{val2}",
new RouteValueDictionary(new {val1 = "", val2 = ""}), new RouteValueDictionary(new {val1 = "", val2 = ""}),
new RouteValueDictionary(new {val1 = "a", val2 = "b"}), new RouteValueDictionary(new {val1 = "a", val2 = "b"}),
"/UrlEncode[[Test]]/UrlEncode[[a]]/UrlEncode[[b]]" "/Test/a/b"
}, },
{ {
"Test/{val1}/{val2}/{val3}", "Test/{val1}/{val2}/{val3}",
new RouteValueDictionary(new {val1 = "", val2 = "", val3 = ""}), new RouteValueDictionary(new {val1 = "", val2 = "", val3 = ""}),
new RouteValueDictionary(new {val1 = "a", val2 = "b", val3 = "c"}), new RouteValueDictionary(new {val1 = "a", val2 = "b", val3 = "c"}),
"/UrlEncode[[Test]]/UrlEncode[[a]]/UrlEncode[[b]]/UrlEncode[[c]]" "/Test/a/b/c"
}, },
{ {
"Test/{val1}/{val2}/{val3}", "Test/{val1}/{val2}/{val3}",
new RouteValueDictionary(new {val1 = "", val2 = "", val3 = ""}), new RouteValueDictionary(new {val1 = "", val2 = "", val3 = ""}),
new RouteValueDictionary(new {val1 = "a", val2 = "b"}), new RouteValueDictionary(new {val1 = "a", val2 = "b"}),
"/UrlEncode[[Test]]/UrlEncode[[a]]/UrlEncode[[b]]" "/Test/a/b"
}, },
{ {
"Test/{val1}/{val2}/{val3}", "Test/{val1}/{val2}/{val3}",
new RouteValueDictionary(new {val1 = "", val2 = "", val3 = ""}), new RouteValueDictionary(new {val1 = "", val2 = "", val3 = ""}),
new RouteValueDictionary(new {val1 = "a"}), new RouteValueDictionary(new {val1 = "a"}),
"/UrlEncode[[Test]]/UrlEncode[[a]]" "/Test/a"
}, },
{ {
"Test/{val1}", "Test/{val1}",
new RouteValueDictionary(new {val1 = "42", val2 = "", val3 = ""}), new RouteValueDictionary(new {val1 = "42", val2 = "", val3 = ""}),
new RouteValueDictionary(), new RouteValueDictionary(),
"/UrlEncode[[Test]]" "/Test"
}, },
{ {
"Test/{val1}/{val2}/{val3}", "Test/{val1}/{val2}/{val3}",
new RouteValueDictionary(new {val1 = "42", val2 = (string)null, val3 = (string)null}), new RouteValueDictionary(new {val1 = "42", val2 = (string)null, val3 = (string)null}),
new RouteValueDictionary(), new RouteValueDictionary(),
"/UrlEncode[[Test]]" "/Test"
}, },
{ {
"Test/{val1}/{val2}/{val3}/{val4}", "Test/{val1}/{val2}/{val3}/{val4}",
new RouteValueDictionary(new {val1 = "21", val2 = "", val3 = "", val4 = ""}), new RouteValueDictionary(new {val1 = "21", val2 = "", val3 = "", val4 = ""}),
new RouteValueDictionary(new {val1 = "42", val2 = "11", val3 = "", val4 = ""}), new RouteValueDictionary(new {val1 = "42", val2 = "11", val3 = "", val4 = ""}),
"/UrlEncode[[Test]]/UrlEncode[[42]]/UrlEncode[[11]]" "/Test/42/11"
}, },
{ {
"Test/{val1}/{val2}/{val3}", "Test/{val1}/{val2}/{val3}",
new RouteValueDictionary(new {val1 = "21", val2 = "", val3 = ""}), new RouteValueDictionary(new {val1 = "21", val2 = "", val3 = ""}),
new RouteValueDictionary(new {val1 = "42"}), new RouteValueDictionary(new {val1 = "42"}),
"/UrlEncode[[Test]]/UrlEncode[[42]]" "/Test/42"
}, },
{ {
"Test/{val1}/{val2}/{val3}/{val4}", "Test/{val1}/{val2}/{val3}/{val4}",
new RouteValueDictionary(new {val1 = "21", val2 = "", val3 = "", val4 = ""}), new RouteValueDictionary(new {val1 = "21", val2 = "", val3 = "", val4 = ""}),
new RouteValueDictionary(new {val1 = "42", val2 = "11"}), new RouteValueDictionary(new {val1 = "42", val2 = "11"}),
"/UrlEncode[[Test]]/UrlEncode[[42]]/UrlEncode[[11]]" "/Test/42/11"
}, },
{ {
"Test/{val1}/{val2}/{val3}", "Test/{val1}/{val2}/{val3}",
new RouteValueDictionary(new {val1 = "21", val2 = (string)null, val3 = (string)null}), new RouteValueDictionary(new {val1 = "21", val2 = (string)null, val3 = (string)null}),
new RouteValueDictionary(new {val1 = "42"}), new RouteValueDictionary(new {val1 = "42"}),
"/UrlEncode[[Test]]/UrlEncode[[42]]" "/Test/42"
}, },
{ {
"Test/{val1}/{val2}/{val3}/{val4}", "Test/{val1}/{val2}/{val3}/{val4}",
new RouteValueDictionary(new {val1 = "21", val2 = (string)null, val3 = (string)null, val4 = (string)null}), new RouteValueDictionary(new {val1 = "21", val2 = (string)null, val3 = (string)null, val4 = (string)null}),
new RouteValueDictionary(new {val1 = "42", val2 = "11"}), new RouteValueDictionary(new {val1 = "42", val2 = "11"}),
"/UrlEncode[[Test]]/UrlEncode[[42]]/UrlEncode[[11]]" "/Test/42/11"
}, },
}; };
@ -116,10 +115,9 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
string expected) string expected)
{ {
// Arrange // Arrange
var encoder = new UrlTestEncoder();
var binder = new TemplateBinder( var binder = new TemplateBinder(
encoder, UrlEncoder.Default,
new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy(encoder)), new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy()),
TemplateParser.Parse(template), TemplateParser.Parse(template),
defaults); defaults);
@ -157,7 +155,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
null, null,
new RouteValueDictionary(new { lang = "en", region = "US" }), new RouteValueDictionary(new { lang = "en", region = "US" }),
new RouteValueDictionary(new { lang = "xx", region = "yy" }), new RouteValueDictionary(new { lang = "xx", region = "yy" }),
"/UrlEncode[[language]]/UrlEncode[[xx]]UrlEncode[[-]]UrlEncode[[yy]]"); "/language/xx-yy");
} }
[Fact] [Fact]
@ -168,7 +166,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
null, null,
new RouteValueDictionary(new { lang = "en", region = "US" }), new RouteValueDictionary(new { lang = "en", region = "US" }),
new RouteValueDictionary(new { lang = "xx", region = "yy" }), new RouteValueDictionary(new { lang = "xx", region = "yy" }),
"/UrlEncode[[language]]/UrlEncode[[xx]]UrlEncode[[-]]UrlEncode[[yy]]UrlEncode[[a]]"); "/language/xx-yya");
} }
[Fact] [Fact]
@ -179,7 +177,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
null, null,
new RouteValueDictionary(new { lang = "en", region = "US" }), new RouteValueDictionary(new { lang = "en", region = "US" }),
new RouteValueDictionary(new { lang = "xx", region = "yy" }), new RouteValueDictionary(new { lang = "xx", region = "yy" }),
"/UrlEncode[[language]]/UrlEncode[[a]]UrlEncode[[xx]]UrlEncode[[-]]UrlEncode[[yy]]"); "/language/axx-yy");
} }
public static TheoryData OptionalParamValues => public static TheoryData OptionalParamValues =>
@ -193,66 +191,66 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
new RouteValueDictionary(new {val1 = "someval1", val2 = "someval2"}), new RouteValueDictionary(new {val1 = "someval1", val2 = "someval2"}),
new RouteValueDictionary(new {val3 = "someval3"}), new RouteValueDictionary(new {val3 = "someval3"}),
new RouteValueDictionary(new {val3 = "someval3"}), new RouteValueDictionary(new {val3 = "someval3"}),
"/UrlEncode[[Test]]/UrlEncode[[someval1]]/UrlEncode[[someval2]]UrlEncode[[.]]UrlEncode[[someval3]]" "/Test/someval1/someval2.someval3"
}, },
{ {
"Test/{val1}/{val2}.{val3?}", "Test/{val1}/{val2}.{val3?}",
new RouteValueDictionary(new {val1 = "someval1", val2 = "someval2"}), new RouteValueDictionary(new {val1 = "someval1", val2 = "someval2"}),
new RouteValueDictionary(new {val3 = "someval3a"}), new RouteValueDictionary(new {val3 = "someval3a"}),
new RouteValueDictionary(new {val3 = "someval3v"}), new RouteValueDictionary(new {val3 = "someval3v"}),
"/UrlEncode[[Test]]/UrlEncode[[someval1]]/UrlEncode[[someval2]]UrlEncode[[.]]UrlEncode[[someval3v]]" "/Test/someval1/someval2.someval3v"
}, },
{ {
"Test/{val1}/{val2}.{val3?}", "Test/{val1}/{val2}.{val3?}",
new RouteValueDictionary(new {val1 = "someval1", val2 = "someval2"}), new RouteValueDictionary(new {val1 = "someval1", val2 = "someval2"}),
new RouteValueDictionary(new {val3 = "someval3a"}), new RouteValueDictionary(new {val3 = "someval3a"}),
new RouteValueDictionary(), new RouteValueDictionary(),
"/UrlEncode[[Test]]/UrlEncode[[someval1]]/UrlEncode[[someval2]]UrlEncode[[.]]UrlEncode[[someval3a]]" "/Test/someval1/someval2.someval3a"
}, },
{ {
"Test/{val1}/{val2}.{val3?}", "Test/{val1}/{val2}.{val3?}",
new RouteValueDictionary(new {val1 = "someval1", val2 = "someval2"}), new RouteValueDictionary(new {val1 = "someval1", val2 = "someval2"}),
new RouteValueDictionary(), new RouteValueDictionary(),
new RouteValueDictionary(new {val3 = "someval3v"}), new RouteValueDictionary(new {val3 = "someval3v"}),
"/UrlEncode[[Test]]/UrlEncode[[someval1]]/UrlEncode[[someval2]]UrlEncode[[.]]UrlEncode[[someval3v]]" "/Test/someval1/someval2.someval3v"
}, },
{ {
"Test/{val1}/{val2}.{val3?}", "Test/{val1}/{val2}.{val3?}",
new RouteValueDictionary(new {val1 = "someval1", val2 = "someval2"}), new RouteValueDictionary(new {val1 = "someval1", val2 = "someval2"}),
new RouteValueDictionary(), new RouteValueDictionary(),
new RouteValueDictionary(), new RouteValueDictionary(),
"/UrlEncode[[Test]]/UrlEncode[[someval1]]/UrlEncode[[someval2]]" "/Test/someval1/someval2"
}, },
{ {
"Test/{val1}.{val2}.{val3}.{val4?}", "Test/{val1}.{val2}.{val3}.{val4?}",
new RouteValueDictionary(new {val1 = "someval1", val2 = "someval2" }), new RouteValueDictionary(new {val1 = "someval1", val2 = "someval2" }),
new RouteValueDictionary(), new RouteValueDictionary(),
new RouteValueDictionary(new {val4 = "someval4", val3 = "someval3" }), new RouteValueDictionary(new {val4 = "someval4", val3 = "someval3" }),
"/UrlEncode[[Test]]/UrlEncode[[someval1]]UrlEncode[[.]]UrlEncode[[someval2]]UrlEncode[[.]]" "/Test/someval1.someval2."
+ "UrlEncode[[someval3]]UrlEncode[[.]]UrlEncode[[someval4]]" + "someval3.someval4"
}, },
{ {
"Test/{val1}.{val2}.{val3}.{val4?}", "Test/{val1}.{val2}.{val3}.{val4?}",
new RouteValueDictionary(new {val1 = "someval1", val2 = "someval2" }), new RouteValueDictionary(new {val1 = "someval1", val2 = "someval2" }),
new RouteValueDictionary(), new RouteValueDictionary(),
new RouteValueDictionary(new {val3 = "someval3" }), new RouteValueDictionary(new {val3 = "someval3" }),
"/UrlEncode[[Test]]/UrlEncode[[someval1]]UrlEncode[[.]]UrlEncode[[someval2]]UrlEncode[[.]]" "/Test/someval1.someval2."
+ "UrlEncode[[someval3]]" + "someval3"
}, },
{ {
"Test/.{val2?}", "Test/.{val2?}",
new RouteValueDictionary(new { }), new RouteValueDictionary(new { }),
new RouteValueDictionary(), new RouteValueDictionary(),
new RouteValueDictionary(new {val2 = "someval2" }), new RouteValueDictionary(new {val2 = "someval2" }),
"/UrlEncode[[Test]]/UrlEncode[[.]]UrlEncode[[someval2]]" "/Test/.someval2"
}, },
{ {
"Test/{val1}.{val2}", "Test/{val1}.{val2}",
new RouteValueDictionary(new {val1 = "someval1", val2 = "someval2" }), new RouteValueDictionary(new {val1 = "someval1", val2 = "someval2" }),
new RouteValueDictionary(), new RouteValueDictionary(),
new RouteValueDictionary(new {val3 = "someval3" }), new RouteValueDictionary(new {val3 = "someval3" }),
"/UrlEncode[[Test]]/UrlEncode[[someval1]]UrlEncode[[.]]UrlEncode[[someval2]]?" + "/Test/someval1.someval2?" +
"UrlEncode[[val3]]=UrlEncode[[someval3]]" "val3=someval3"
}, },
}; };
@ -266,10 +264,9 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
string expected) string expected)
{ {
// Arrange // Arrange
var encoder = new UrlTestEncoder();
var binder = new TemplateBinder( var binder = new TemplateBinder(
encoder, UrlEncoder.Default,
new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy(encoder)), new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy()),
TemplateParser.Parse(template), TemplateParser.Parse(template),
defaults); defaults);
@ -307,7 +304,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
null, null,
new RouteValueDictionary(new { lang = "en", region = "US" }), new RouteValueDictionary(new { lang = "en", region = "US" }),
new RouteValueDictionary(new { lang = "xx", region = "yy" }), new RouteValueDictionary(new { lang = "xx", region = "yy" }),
"/UrlEncode[[language]]/UrlEncode[[a]]UrlEncode[[xx]]UrlEncode[[-]]UrlEncode[[yy]]UrlEncode[[a]]"); "/language/axx-yya");
} }
[Fact] [Fact]
@ -340,7 +337,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
null, null,
new RouteValueDictionary(new { lang = "en" }), new RouteValueDictionary(new { lang = "en" }),
new RouteValueDictionary(new { lang = "xx" }), new RouteValueDictionary(new { lang = "xx" }),
"/UrlEncode[[language]]/UrlEncode[[xx]]"); "/language/xx");
} }
[Fact] [Fact]
@ -351,7 +348,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
null, null,
new RouteValueDictionary(new { lang = "en" }), new RouteValueDictionary(new { lang = "en" }),
new RouteValueDictionary(new { lang = "xx" }), new RouteValueDictionary(new { lang = "xx" }),
"/UrlEncode[[language]]/UrlEncode[[xx]]UrlEncode[[-]]"); "/language/xx-");
} }
[Fact] [Fact]
@ -362,7 +359,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
null, null,
new RouteValueDictionary(new { lang = "en" }), new RouteValueDictionary(new { lang = "en" }),
new RouteValueDictionary(new { lang = "xx" }), new RouteValueDictionary(new { lang = "xx" }),
"/UrlEncode[[language]]/UrlEncode[[a]]UrlEncode[[xx]]"); "/language/axx");
} }
[Fact] [Fact]
@ -373,7 +370,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
null, null,
new RouteValueDictionary(new { lang = "en" }), new RouteValueDictionary(new { lang = "en" }),
new RouteValueDictionary(new { lang = "xx" }), new RouteValueDictionary(new { lang = "xx" }),
"/UrlEncode[[language]]/UrlEncode[[a]]UrlEncode[[xx]]UrlEncode[[a]]"); "/language/axxa");
} }
[Fact] [Fact]
@ -384,7 +381,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
new RouteValueDictionary(new { action = "Index", id = (string)null }), new RouteValueDictionary(new { action = "Index", id = (string)null }),
new RouteValueDictionary(new { controller = "home", action = "list", id = (string)null }), new RouteValueDictionary(new { controller = "home", action = "list", id = (string)null }),
new RouteValueDictionary(new { controller = "products" }), new RouteValueDictionary(new { controller = "products" }),
"/UrlEncode[[products]]UrlEncode[[.mvc]]"); "/products.mvc");
} }
[Fact] [Fact]
@ -395,7 +392,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
new RouteValueDictionary(new { lang = "xx", region = "yy" }), new RouteValueDictionary(new { lang = "xx", region = "yy" }),
new RouteValueDictionary(new { lang = "en", region = "US" }), new RouteValueDictionary(new { lang = "en", region = "US" }),
new RouteValueDictionary(new { lang = "zz" }), new RouteValueDictionary(new { lang = "zz" }),
"/UrlEncode[[language]]/UrlEncode[[zz]]UrlEncode[[-]]UrlEncode[[yy]]"); "/language/zz-yy");
} }
[Fact] [Fact]
@ -407,7 +404,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
new RouteValueDictionary(new { id = "defaultid" }), new RouteValueDictionary(new { id = "defaultid" }),
new RouteValueDictionary(new { controller = "home", action = "oldaction" }), new RouteValueDictionary(new { controller = "home", action = "oldaction" }),
new RouteValueDictionary(new { action = "newaction" }), new RouteValueDictionary(new { action = "newaction" }),
"/UrlEncode[[home]]/UrlEncode[[newaction]]"); "/home/newaction");
} }
[Fact] [Fact]
@ -440,7 +437,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
null, null,
new RouteValueDictionary(new { }), new RouteValueDictionary(new { }),
new RouteValueDictionary(new { controller = "home" }), new RouteValueDictionary(new { controller = "home" }),
"/UrlEncode[[foo]]/UrlEncode[[home]]"); "/foo/home");
} }
[Fact] [Fact]
@ -452,7 +449,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
new RouteValueDictionary(new { id = (string)null }), new RouteValueDictionary(new { id = (string)null }),
new RouteValueDictionary(new { controller = "home", action = "oldaction", id = (string)null }), new RouteValueDictionary(new { controller = "home", action = "oldaction", id = (string)null }),
new RouteValueDictionary(new { action = "newaction" }), new RouteValueDictionary(new { action = "newaction" }),
"/UrlEncode[[home]]/UrlEncode[[newaction]]"); "/home/newaction");
} }
[Fact] [Fact]
@ -463,7 +460,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
new RouteValueDictionary(new { language = "en", locale = "US" }), new RouteValueDictionary(new { language = "en", locale = "US" }),
new RouteValueDictionary(), new RouteValueDictionary(),
new RouteValueDictionary(new { controller = "Orders" }), new RouteValueDictionary(new { controller = "Orders" }),
"/UrlEncode[[Orders]]/UrlEncode[[en]]UrlEncode[[-]]UrlEncode[[US]]"); "/Orders/en-US");
} }
[Fact] [Fact]
@ -474,7 +471,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
new RouteValueDictionary(new { action = "Index", id = "" }), new RouteValueDictionary(new { action = "Index", id = "" }),
new RouteValueDictionary(new { controller = "Home", action = "Index", id = "" }), new RouteValueDictionary(new { controller = "Home", action = "Index", id = "" }),
new RouteValueDictionary(new { controller = "Home", action = "TestAction", id = "1", format = (string)null }), new RouteValueDictionary(new { controller = "Home", action = "TestAction", id = "1", format = (string)null }),
"/UrlEncode[[Home]]UrlEncode[[.mvc]]/UrlEncode[[TestAction]]/UrlEncode[[1]]"); "/Home.mvc/TestAction/1");
} }
[Fact] [Fact]
@ -507,7 +504,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
new { p2 = "d2", p3 = "d3" }, new { p2 = "d2", p3 = "d3" },
new { p1 = "v1", }, new { p1 = "v1", },
new { p2 = "", p3 = "" }, new { p2 = "", p3 = "" },
"/UrlEncode[[v1]]"); "/v1");
} }
[Fact] [Fact]
@ -518,7 +515,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
new { action = "Index", id = (string)null }, new { action = "Index", id = (string)null },
new { controller = "orig", action = "init", id = "123" }, new { controller = "orig", action = "init", id = "123" },
new { action = "new", }, new { action = "new", },
"/UrlEncode[[orig]]/UrlEncode[[new]]"); "/orig/new");
} }
[Fact] [Fact]
@ -529,8 +526,8 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
new { year = 1995, occasion = "Christmas", action = "Play", SafeParam = "SafeParamValue" }, new { year = 1995, occasion = "Christmas", action = "Play", SafeParam = "SafeParamValue" },
new { controller = "UrlRouting", action = "Play", category = "Photos", year = "2008", occasion = "Easter", SafeParam = "SafeParamValue" }, new { controller = "UrlRouting", action = "Play", category = "Photos", year = "2008", occasion = "Easter", SafeParam = "SafeParamValue" },
new { year = (string)null, occasion = "Hola" }, new { year = (string)null, occasion = "Hola" },
"/UrlEncode[[UrlGeneration1]]/UrlEncode[[UrlRouting]]UrlEncode[[.mvc]]/UrlEncode[[Play]]/" "/UrlGeneration1/UrlRouting.mvc/Play/"
+ "UrlEncode[[Photos]]/UrlEncode[[1995]]/UrlEncode[[Hola]]"); + "Photos/1995/Hola");
} }
[Fact] [Fact]
@ -553,8 +550,8 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
new RouteValueDictionary(new { year = 1995, occasion = "Christmas", action = "Play", SafeParam = "SafeParamValue" }), new RouteValueDictionary(new { year = 1995, occasion = "Christmas", action = "Play", SafeParam = "SafeParamValue" }),
ambientValues, ambientValues,
values, values,
"/UrlEncode[[UrlGeneration1]]/UrlEncode[[UrlRouting]]UrlEncode[[.mvc]]/" "/UrlGeneration1/UrlRouting.mvc/"
+ "UrlEncode[[Play]]/UrlEncode[[Photos]]/UrlEncode[[1995]]/UrlEncode[[Hola]]"); + "Play/Photos/1995/Hola");
} }
[Fact] [Fact]
@ -576,7 +573,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
new RouteValueDictionary(new { action = "Default" }), new RouteValueDictionary(new { action = "Default" }),
ambientValues, ambientValues,
values, values,
"/UrlEncode[[subtest]]UrlEncode[[.mvc]]/UrlEncode[[Default]]/UrlEncode[[b]]"); "/subtest.mvc/Default/b");
} }
[Fact] [Fact]
@ -594,8 +591,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
new RouteValueDictionary(new { controller = "Home" }), new RouteValueDictionary(new { controller = "Home" }),
new RouteValueDictionary(new { controller = "home", action = "Index", id = (string)null }), new RouteValueDictionary(new { controller = "home", action = "Index", id = (string)null }),
values, values,
"/%23;%3F%3A@%26%3D%2B$,.mvc/showcategory/123?so%3Frt=de%3Fsc&maxPrice=100", "/%23;%3F%3A@%26%3D%2B$,.mvc/showcategory/123?so%3Frt=de%3Fsc&maxPrice=100");
UrlEncoder.Default);
} }
[Fact] [Fact]
@ -609,8 +605,8 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
new RouteValueDictionary(new { controller = "Home" }), new RouteValueDictionary(new { controller = "Home" }),
new RouteValueDictionary(new { controller = "home", action = "Index", id = (string)null }), new RouteValueDictionary(new { controller = "home", action = "Index", id = (string)null }),
values, values,
"/UrlEncode[[products]]UrlEncode[[.mvc]]/UrlEncode[[showcategory]]/UrlEncode[[123]]" + "/products.mvc/showcategory/123" +
"?UrlEncode[[so?rt]]=UrlEncode[[de?sc]]&UrlEncode[[maxPrice]]=UrlEncode[[100]]"); "?so%3Frt=de%3Fsc&maxPrice=100");
} }
[Fact] [Fact]
@ -630,8 +626,8 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
maxPrice = 100, maxPrice = 100,
custom = "customValue" custom = "customValue"
}), }),
"/UrlEncode[[products]]UrlEncode[[.mvc]]/UrlEncode[[showcategory]]/UrlEncode[[123]]" + "/products.mvc/showcategory/123" +
"?UrlEncode[[sort]]=UrlEncode[[desc]]&UrlEncode[[maxPrice]]=UrlEncode[[100]]"); "?sort=desc&maxPrice=100");
} }
[Fact] [Fact]
@ -642,8 +638,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
null, null,
new RouteValueDictionary(new { controller = "ho%me", action = "li st" }), new RouteValueDictionary(new { controller = "ho%me", action = "li st" }),
new RouteValueDictionary(), new RouteValueDictionary(),
"/bl%25og/ho%25me/he%20llo/li%20st", "/bl%25og/ho%25me/he%20llo/li%20st");
UrlEncoder.Default);
} }
[Fact] [Fact]
@ -654,8 +649,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
null, null,
new RouteValueDictionary(new { controller = "/home", action = "/my/index" }), new RouteValueDictionary(new { controller = "/home", action = "/my/index" }),
new RouteValueDictionary(), new RouteValueDictionary(),
"/home/%2Fmy%2Findex", "/home/%2Fmy%2Findex");
UrlEncoder.Default);
} }
[Fact] [Fact]
@ -666,7 +660,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
new RouteValueDictionary(new { id = "defaultid" }), new RouteValueDictionary(new { id = "defaultid" }),
new RouteValueDictionary(new { p1 = "v1" }), new RouteValueDictionary(new { p1 = "v1" }),
new RouteValueDictionary(new { p2 = "v2a/v2b" }), new RouteValueDictionary(new { p2 = "v2a/v2b" }),
"/UrlEncode[[v1]]/UrlEncode[[v2a/v2b]]"); "/v1/v2a%2Fv2b");
} }
[Fact] [Fact]
@ -677,7 +671,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
new RouteValueDictionary(new { id = "defaultid" }), new RouteValueDictionary(new { id = "defaultid" }),
new RouteValueDictionary(new { p1 = "v1" }), new RouteValueDictionary(new { p1 = "v1" }),
new RouteValueDictionary(new { p2 = "" }), new RouteValueDictionary(new { p2 = "" }),
"/UrlEncode[[v1]]"); "/v1");
} }
[Fact] [Fact]
@ -688,7 +682,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
new RouteValueDictionary(new { id = "defaultid" }), new RouteValueDictionary(new { id = "defaultid" }),
new RouteValueDictionary(new { p1 = "v1" }), new RouteValueDictionary(new { p1 = "v1" }),
new RouteValueDictionary(new { p2 = (string)null }), new RouteValueDictionary(new { p2 = (string)null }),
"/UrlEncode[[v1]]"); "/v1");
} }
[Fact] [Fact]
@ -699,7 +693,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
null, null,
null, null,
new RouteValueDictionary(new { }), new RouteValueDictionary(new { }),
"/UrlEncode[[foo]]"); "/foo");
} }
[Fact] [Fact]
@ -710,7 +704,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
null, null,
null, null,
new RouteValueDictionary(new { }), new RouteValueDictionary(new { }),
"/UrlEncode[[foo]]"); "/foo");
} }
[Fact] [Fact]
@ -718,10 +712,9 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
{ {
// Arrange // Arrange
var template = "{area?}/{controller=Home}/{action=Index}/{id?}"; var template = "{area?}/{controller=Home}/{action=Index}/{id?}";
var encoder = new UrlTestEncoder();
var binder = new TemplateBinder( var binder = new TemplateBinder(
new UrlTestEncoder(), UrlEncoder.Default,
new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy(encoder)), new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy()),
TemplateParser.Parse(template), TemplateParser.Parse(template),
defaults: null); defaults: null);
var ambientValues = new RouteValueDictionary(); var ambientValues = new RouteValueDictionary();
@ -1145,15 +1138,12 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
RouteValueDictionary defaults, RouteValueDictionary defaults,
RouteValueDictionary ambientValues, RouteValueDictionary ambientValues,
RouteValueDictionary values, RouteValueDictionary values,
string expected, string expected)
UrlEncoder encoder = null)
{ {
// Arrange // Arrange
encoder = encoder ?? new UrlTestEncoder();
var binder = new TemplateBinder( var binder = new TemplateBinder(
encoder, UrlEncoder.Default,
new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy(encoder)), new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy()),
TemplateParser.Parse(template), TemplateParser.Parse(template),
defaults); defaults);

View File

@ -244,13 +244,12 @@ namespace Microsoft.AspNetCore.Routing.Tree
private static TreeRouteBuilder CreateBuilder() private static TreeRouteBuilder CreateBuilder()
{ {
var objectPoolProvider = new DefaultObjectPoolProvider(); var objectPoolProvider = new DefaultObjectPoolProvider();
var objectPolicy = new UriBuilderContextPooledObjectPolicy(UrlEncoder.Default); var objectPolicy = new UriBuilderContextPooledObjectPolicy();
var objectPool = objectPoolProvider.Create(objectPolicy); var objectPool = objectPoolProvider.Create(objectPolicy);
var constraintResolver = GetInlineConstraintResolver(); var constraintResolver = GetInlineConstraintResolver();
var builder = new TreeRouteBuilder( var builder = new TreeRouteBuilder(
NullLoggerFactory.Instance, NullLoggerFactory.Instance,
UrlEncoder.Default,
objectPool, objectPool,
constraintResolver); constraintResolver);
return builder; return builder;

View File

@ -13,7 +13,6 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.Extensions.WebEncoders.Testing;
using Moq; using Moq;
using Xunit; using Xunit;
@ -23,9 +22,72 @@ namespace Microsoft.AspNetCore.Routing.Tree
{ {
private static readonly RequestDelegate NullHandler = (c) => Task.FromResult(0); private static readonly RequestDelegate NullHandler = (c) => Task.FromResult(0);
private static UrlEncoder Encoder = UrlTestEncoder.Default;
private static ObjectPool<UriBuildingContext> Pool = new DefaultObjectPoolProvider().Create( private static ObjectPool<UriBuildingContext> Pool = new DefaultObjectPoolProvider().Create(
new UriBuilderContextPooledObjectPolicy(Encoder)); new UriBuilderContextPooledObjectPolicy());
[Fact]
public async Task TreeRouter_RouteAsync_MatchesCatchAllRoutesWithDefaults_UsingObsoleteConstructo()
{
// Arrange
var routes = new[] {
"{parameter1=1}/{parameter2=2}/{parameter3=3}/{*parameter4=4}",
};
var url = "/a/b/c";
var routeValues = new[] { "a", "b", "c", "4" };
var expectedRouteGroup = CreateRouteGroup(0, "{parameter1=1}/{parameter2=2}/{parameter3=3}/{*parameter4=4}");
var routeValueKeys = new[] { "parameter1", "parameter2", "parameter3", "parameter4" };
var expectedRouteValues = new RouteValueDictionary();
for (int i = 0; i < routeValueKeys.Length; i++)
{
expectedRouteValues.Add(routeValueKeys[i], routeValues[i]);
}
var builder = CreateBuilderUsingObsoleteConstructor();
// We setup the route entries in reverse order of precedence to ensure that when we
// try to route the request, the route with a higher precedence gets tried first.
foreach (var template in routes.Reverse())
{
MapInboundEntry(builder, template);
}
var route = builder.Build();
var context = CreateRouteContext(url);
// Act
await route.RouteAsync(context);
// Assert
Assert.Equal(expectedRouteGroup, context.RouteData.Values["test_route_group"]);
foreach (var entry in expectedRouteValues)
{
var data = Assert.Single(context.RouteData.Values, v => v.Key == entry.Key);
Assert.Equal(entry.Value, data.Value);
}
}
[Fact]
public async Task TreeRouter_RouteAsync_DoesNotMatchRoutesWithIntermediateDefaultRouteValues_UsingObsoleteConstructor()
{
// Arrange
var url = "/a/b";
var builder = CreateBuilderUsingObsoleteConstructor();
MapInboundEntry(builder, "a/b/{parameter3=3}/d");
var route = builder.Build();
var context = CreateRouteContext(url);
// Act
await route.RouteAsync(context);
// Assert
Assert.Null(context.Handler);
}
[Theory] [Theory]
[InlineData("template/5", "template/{parameter:int}")] [InlineData("template/5", "template/{parameter:int}")]
@ -1990,15 +2052,31 @@ namespace Microsoft.AspNetCore.Routing.Tree
private static TreeRouteBuilder CreateBuilder() private static TreeRouteBuilder CreateBuilder()
{ {
var objectPoolProvider = new DefaultObjectPoolProvider(); var objectPoolProvider = new DefaultObjectPoolProvider();
var objectPolicy = new UriBuilderContextPooledObjectPolicy(UrlEncoder.Default); var objectPolicy = new UriBuilderContextPooledObjectPolicy();
var objectPool = objectPoolProvider.Create<UriBuildingContext>(objectPolicy); var objectPool = objectPoolProvider.Create<UriBuildingContext>(objectPolicy);
var constraintResolver = CreateConstraintResolver(); var constraintResolver = CreateConstraintResolver();
var builder = new TreeRouteBuilder(
NullLoggerFactory.Instance,
objectPool,
constraintResolver);
return builder;
}
private static TreeRouteBuilder CreateBuilderUsingObsoleteConstructor()
{
var objectPoolProvider = new DefaultObjectPoolProvider();
var objectPolicy = new UriBuilderContextPooledObjectPolicy();
var objectPool = objectPoolProvider.Create<UriBuildingContext>(objectPolicy);
var constraintResolver = CreateConstraintResolver();
#pragma warning disable CS0618 // Type or member is obsolete
var builder = new TreeRouteBuilder( var builder = new TreeRouteBuilder(
NullLoggerFactory.Instance, NullLoggerFactory.Instance,
UrlEncoder.Default, UrlEncoder.Default,
objectPool, objectPool,
constraintResolver); constraintResolver);
#pragma warning restore CS0618 // Type or member is obsolete
return builder; return builder;
} }