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 BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Internal;
using Microsoft.AspNetCore.Routing.Template;
using Microsoft.AspNetCore.Routing.Tree;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options;
using System.Diagnostics;
namespace Microsoft.AspNetCore.Routing.Performance
{
@ -33,8 +30,7 @@ namespace Microsoft.AspNetCore.Routing.Performance
var treeBuilder = new TreeRouteBuilder(
NullLoggerFactory.Instance,
UrlEncoder.Default,
new DefaultObjectPool<UriBuildingContext>(new UriBuilderContextPooledObjectPolicy(UrlEncoder.Default)),
new DefaultObjectPool<UriBuildingContext>(new UriBuilderContextPooledObjectPolicy()),
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);

View File

@ -2,11 +2,11 @@
// 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 Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Internal;
using Microsoft.AspNetCore.Routing.Tree;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.ObjectPool;
namespace Microsoft.Extensions.DependencyInjection
@ -29,17 +29,21 @@ namespace Microsoft.Extensions.DependencyInjection
}
services.TryAddTransient<IInlineConstraintResolver, DefaultInlineConstraintResolver>();
services.TryAddSingleton(UrlEncoder.Default);
services.TryAddSingleton<ObjectPool<UriBuildingContext>>(s =>
{
var provider = s.GetRequiredService<ObjectPoolProvider>();
var encoder = s.GetRequiredService<UrlEncoder>();
return provider.Create<UriBuildingContext>(new UriBuilderContextPooledObjectPolicy(encoder));
return provider.Create<UriBuildingContext>(new UriBuilderContextPooledObjectPolicy());
});
// The TreeRouteBuilder is a builder for creating routes, it should stay transient because it's
// 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));

View File

@ -1,7 +1,6 @@
// 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.Encodings.Web;
using Microsoft.Extensions.ObjectPool;
@ -9,21 +8,9 @@ namespace Microsoft.AspNetCore.Routing.Internal
{
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()
{
return new UriBuildingContext(_encoder);
return new UriBuildingContext(UrlEncoder.Default);
}
public bool Return(UriBuildingContext obj)

View File

@ -241,9 +241,8 @@ namespace Microsoft.AspNetCore.Routing
{
if (_binder == null)
{
var urlEncoder = context.RequestServices.GetRequiredService<UrlEncoder>();
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 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(
UrlEncoder urlEncoder,
ObjectPool<UriBuildingContext> pool,
@ -90,8 +97,8 @@ namespace Microsoft.AspNetCore.Routing.Template
}
}
if (!hasNewParameterValue &&
!hasCurrentParameterValue &&
if (!hasNewParameterValue &&
!hasCurrentParameterValue &&
_defaults?.ContainsKey(parameter.Name) != true)
{
// 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;
/// <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>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
/// <param name="urlEncoder">The <see cref="UrlEncoder"/>.</param>
/// <param name="objectPool">The <see cref="ObjectPool{UrlBuildingContext}"/>.</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(
ILoggerFactory loggerFactory,
UrlEncoder urlEncoder,
ObjectPool<UriBuildingContext> objectPool,
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)
{
throw new ArgumentNullException(nameof(loggerFactory));
}
if (urlEncoder == null)
{
throw new ArgumentNullException(nameof(urlEncoder));
}
if (objectPool == null)
{
throw new ArgumentNullException(nameof(objectPool));
@ -57,7 +78,7 @@ namespace Microsoft.AspNetCore.Routing.Tree
throw new ArgumentNullException(nameof(constraintResolver));
}
_urlEncoder = urlEncoder;
_urlEncoder = UrlEncoder.Default;
_objectPool = objectPool;
_constraintResolver = constraintResolver;

View File

@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
@ -14,6 +15,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.WebEncoders.Testing;
using Moq;
using Xunit;
@ -647,6 +649,37 @@ namespace Microsoft.AspNetCore.Routing
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]
public void GetVirtualPath_ForListOfStrings()
{

View File

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

View File

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

View File

@ -13,7 +13,6 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.WebEncoders.Testing;
using Moq;
using Xunit;
@ -23,9 +22,72 @@ namespace Microsoft.AspNetCore.Routing.Tree
{
private static readonly RequestDelegate NullHandler = (c) => Task.FromResult(0);
private static UrlEncoder Encoder = UrlTestEncoder.Default;
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]
[InlineData("template/5", "template/{parameter:int}")]
@ -1990,15 +2052,31 @@ namespace Microsoft.AspNetCore.Routing.Tree
private static TreeRouteBuilder CreateBuilder()
{
var objectPoolProvider = new DefaultObjectPoolProvider();
var objectPolicy = new UriBuilderContextPooledObjectPolicy(UrlEncoder.Default);
var objectPolicy = new UriBuilderContextPooledObjectPolicy();
var objectPool = objectPoolProvider.Create<UriBuildingContext>(objectPolicy);
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(
NullLoggerFactory.Instance,
UrlEncoder.Default,
objectPool,
constraintResolver);
#pragma warning restore CS0618 // Type or member is obsolete
return builder;
}