[Fixes #357] Fix behavior when optional parameters are defined within a route and not at the end
This commit is contained in:
parent
438ec83227
commit
74a3063c45
|
|
@ -5,7 +5,8 @@
|
||||||
"Microsoft.AspNetCore.Routing": "1.1.0-*"
|
"Microsoft.AspNetCore.Routing": "1.1.0-*"
|
||||||
},
|
},
|
||||||
"buildOptions": {
|
"buildOptions": {
|
||||||
"emitEntryPoint": true
|
"emitEntryPoint": true,
|
||||||
|
"keyFile": "../../tools/Key.snk"
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
"net451": {},
|
"net451": {},
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,11 @@
|
||||||
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Resources;
|
using System.Resources;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
[assembly: AssemblyMetadata("Serviceable", "True")]
|
[assembly: AssemblyMetadata("Serviceable", "True")]
|
||||||
[assembly: NeutralResourcesLanguage("en-us")]
|
[assembly: NeutralResourcesLanguage("en-us")]
|
||||||
[assembly: AssemblyCompany("Microsoft Corporation.")]
|
[assembly: AssemblyCompany("Microsoft Corporation.")]
|
||||||
[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
|
[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
|
||||||
[assembly: AssemblyProduct("Microsoft ASP.NET Core")]
|
[assembly: AssemblyProduct("Microsoft ASP.NET Core")]
|
||||||
|
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Routing.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
|
|
|
||||||
|
|
@ -333,7 +333,13 @@ namespace Microsoft.AspNetCore.Routing.Tree
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (part.IsParameter && (part.IsOptional || part.IsCatchAll || part.DefaultValue != null))
|
// We accept templates that have intermediate optional values, but we ignore
|
||||||
|
// those values for route matching. For that reason, we need to add the entry
|
||||||
|
// to the list of matches, only if the remaining segments are optional. For example:
|
||||||
|
// /{controller}/{action=Index}/{id} will be equivalent to /{controller}/{action}/{id}
|
||||||
|
// for the purposes of route matching.
|
||||||
|
if (part.IsParameter &&
|
||||||
|
RemainingSegmentsAreOptional(entry.RouteTemplate.Segments, i))
|
||||||
{
|
{
|
||||||
current.Matches.Add(new InboundMatch() { Entry = entry, TemplateMatcher = matcher });
|
current.Matches.Add(new InboundMatch() { Entry = entry, TemplateMatcher = matcher });
|
||||||
}
|
}
|
||||||
|
|
@ -392,5 +398,36 @@ namespace Microsoft.AspNetCore.Routing.Tree
|
||||||
return result == 0 ? x.Entry.RouteTemplate.TemplateText.CompareTo(y.Entry.RouteTemplate.TemplateText) : result;
|
return result == 0 ? x.Entry.RouteTemplate.TemplateText.CompareTo(y.Entry.RouteTemplate.TemplateText) : result;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool RemainingSegmentsAreOptional(IList<TemplateSegment> segments, int currentParameterIndex)
|
||||||
|
{
|
||||||
|
for (var i = currentParameterIndex; i < segments.Count; i++)
|
||||||
|
{
|
||||||
|
if (!segments[i].IsSimple)
|
||||||
|
{
|
||||||
|
// /{complex}-{segment}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var part = segments[i].Parts[0];
|
||||||
|
if (!part.IsParameter)
|
||||||
|
{
|
||||||
|
// /literal
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isOptionlCatchAllOrHasDefaultValue = part.IsOptional ||
|
||||||
|
part.IsCatchAll ||
|
||||||
|
part.DefaultValue != null;
|
||||||
|
|
||||||
|
if (!isOptionlCatchAllOrHasDefaultValue)
|
||||||
|
{
|
||||||
|
// /{parameter}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Routing.Internal;
|
using Microsoft.AspNetCore.Routing.Internal;
|
||||||
using Microsoft.AspNetCore.Routing.Logging;
|
using Microsoft.AspNetCore.Routing.Logging;
|
||||||
using Microsoft.AspNetCore.Routing.Template;
|
using Microsoft.AspNetCore.Routing.Template;
|
||||||
|
|
@ -110,7 +109,7 @@ namespace Microsoft.AspNetCore.Routing.Tree
|
||||||
if (_namedEntries.TryGetValue(entry.RouteName, out namedMatch) &&
|
if (_namedEntries.TryGetValue(entry.RouteName, out namedMatch) &&
|
||||||
!string.Equals(
|
!string.Equals(
|
||||||
namedMatch.Entry.RouteTemplate.TemplateText,
|
namedMatch.Entry.RouteTemplate.TemplateText,
|
||||||
entry.RouteTemplate.TemplateText,
|
entry.RouteTemplate.TemplateText,
|
||||||
StringComparison.OrdinalIgnoreCase))
|
StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
throw new ArgumentException(
|
throw new ArgumentException(
|
||||||
|
|
@ -134,6 +133,8 @@ namespace Microsoft.AspNetCore.Routing.Tree
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Version { get; }
|
public int Version { get; }
|
||||||
|
|
||||||
|
internal IEnumerable<UrlMatchingTree> MatchingTrees => _trees;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public VirtualPathData GetVirtualPath(VirtualPathContext context)
|
public VirtualPathData GetVirtualPath(VirtualPathContext context)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"buildOptions": {
|
"buildOptions": {
|
||||||
"warningsAsErrors": true
|
"warningsAsErrors": true,
|
||||||
|
"keyFile": "../../tools/Key.snk"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dotnet-test-xunit": "2.2.0-*",
|
"dotnet-test-xunit": "2.2.0-*",
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,14 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
|
using Microsoft.AspNetCore.Routing.Constraints;
|
||||||
using Microsoft.AspNetCore.Routing.Internal;
|
using Microsoft.AspNetCore.Routing.Internal;
|
||||||
using Microsoft.AspNetCore.Routing.Template;
|
using Microsoft.AspNetCore.Routing.Template;
|
||||||
using Microsoft.AspNetCore.Testing;
|
using Microsoft.AspNetCore.Testing;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging.Testing;
|
using Microsoft.Extensions.Logging.Testing;
|
||||||
using Microsoft.Extensions.ObjectPool;
|
using Microsoft.Extensions.ObjectPool;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -70,13 +73,185 @@ namespace Microsoft.AspNetCore.Routing.Tree
|
||||||
builder.Build();
|
builder.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TreeRouter_BuildDoesNotAddIntermediateMatchingNodes_ForRoutesWithIntermediateParametersWithDefaultValues()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var builder = CreateBuilder();
|
||||||
|
|
||||||
|
builder.MapInbound(
|
||||||
|
Mock.Of<IRouter>(),
|
||||||
|
TemplateParser.Parse("a/{b=3}/c"),
|
||||||
|
"Intermediate",
|
||||||
|
order: 0);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tree = builder.Build();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(tree);
|
||||||
|
Assert.NotNull(tree.MatchingTrees);
|
||||||
|
var matchingTree = Assert.Single(tree.MatchingTrees);
|
||||||
|
|
||||||
|
var firstSegment = Assert.Single(matchingTree.Root.Literals);
|
||||||
|
Assert.Equal("a", firstSegment.Key);
|
||||||
|
Assert.NotNull(firstSegment.Value.Parameters);
|
||||||
|
|
||||||
|
var secondSegment = firstSegment.Value.Parameters;
|
||||||
|
Assert.Empty(secondSegment.Matches);
|
||||||
|
|
||||||
|
var thirdSegment = Assert.Single(secondSegment.Literals);
|
||||||
|
Assert.Equal("c", thirdSegment.Key);
|
||||||
|
Assert.Single(thirdSegment.Value.Matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TreeRouter_BuildDoesNotAddIntermediateMatchingNodes_ForRoutesWithMultipleIntermediateParametersWithDefaultOrOptionalValues()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var builder = CreateBuilder();
|
||||||
|
|
||||||
|
builder.MapInbound(
|
||||||
|
Mock.Of<IRouter>(),
|
||||||
|
TemplateParser.Parse("a/{b=3}/c/{d?}/e/{*f}"),
|
||||||
|
"Intermediate",
|
||||||
|
order: 0);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tree = builder.Build();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(tree);
|
||||||
|
Assert.NotNull(tree.MatchingTrees);
|
||||||
|
var matchingTree = Assert.Single(tree.MatchingTrees);
|
||||||
|
|
||||||
|
var firstSegment = Assert.Single(matchingTree.Root.Literals);
|
||||||
|
Assert.Equal("a", firstSegment.Key);
|
||||||
|
Assert.NotNull(firstSegment.Value.Parameters);
|
||||||
|
|
||||||
|
var secondSegment = firstSegment.Value.Parameters;
|
||||||
|
Assert.Empty(secondSegment.Matches);
|
||||||
|
|
||||||
|
var thirdSegment = Assert.Single(secondSegment.Literals);
|
||||||
|
Assert.Equal("c", thirdSegment.Key);
|
||||||
|
Assert.Empty(thirdSegment.Value.Matches);
|
||||||
|
|
||||||
|
var fourthSegment = thirdSegment.Value.Parameters;
|
||||||
|
Assert.NotNull(fourthSegment);
|
||||||
|
Assert.Empty(fourthSegment.Matches);
|
||||||
|
|
||||||
|
var fifthSegment = Assert.Single(fourthSegment.Literals);
|
||||||
|
Assert.Equal("e", fifthSegment.Key);
|
||||||
|
Assert.Single(fifthSegment.Value.Matches);
|
||||||
|
|
||||||
|
var sixthSegment = fifthSegment.Value.CatchAlls;
|
||||||
|
Assert.NotNull(sixthSegment);
|
||||||
|
Assert.Single(sixthSegment.Matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TreeRouter_BuildDoesNotAddIntermediateMatchingNodes_ForRoutesWithIntermediateParametersWithOptionalValues()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var builder = CreateBuilder();
|
||||||
|
|
||||||
|
builder.MapInbound(
|
||||||
|
Mock.Of<IRouter>(),
|
||||||
|
TemplateParser.Parse("a/{b?}/c"),
|
||||||
|
"Intermediate",
|
||||||
|
order: 0);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tree = builder.Build();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(tree);
|
||||||
|
Assert.NotNull(tree.MatchingTrees);
|
||||||
|
var matchingTree = Assert.Single(tree.MatchingTrees);
|
||||||
|
|
||||||
|
var firstSegment = Assert.Single(matchingTree.Root.Literals);
|
||||||
|
Assert.Equal("a", firstSegment.Key);
|
||||||
|
Assert.NotNull(firstSegment.Value.Parameters);
|
||||||
|
|
||||||
|
var secondSegment = firstSegment.Value.Parameters;
|
||||||
|
Assert.Empty(secondSegment.Matches);
|
||||||
|
|
||||||
|
var thirdSegment = Assert.Single(secondSegment.Literals);
|
||||||
|
Assert.Equal("c", thirdSegment.Key);
|
||||||
|
Assert.Single(thirdSegment.Value.Matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TreeRouter_BuildDoesNotAddIntermediateMatchingNodes_ForRoutesWithIntermediateParametersWithConstrainedDefaultValues()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var builder = CreateBuilder();
|
||||||
|
|
||||||
|
builder.MapInbound(
|
||||||
|
Mock.Of<IRouter>(),
|
||||||
|
TemplateParser.Parse("a/{b:int=3}/c"),
|
||||||
|
"Intermediate",
|
||||||
|
order: 0);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tree = builder.Build();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(tree);
|
||||||
|
Assert.NotNull(tree.MatchingTrees);
|
||||||
|
var matchingTree = Assert.Single(tree.MatchingTrees);
|
||||||
|
|
||||||
|
var firstSegment = Assert.Single(matchingTree.Root.Literals);
|
||||||
|
Assert.Equal("a", firstSegment.Key);
|
||||||
|
Assert.NotNull(firstSegment.Value.ConstrainedParameters);
|
||||||
|
|
||||||
|
var secondSegment = firstSegment.Value.ConstrainedParameters;
|
||||||
|
Assert.Empty(secondSegment.Matches);
|
||||||
|
|
||||||
|
var thirdSegment = Assert.Single(secondSegment.Literals);
|
||||||
|
Assert.Equal("c", thirdSegment.Key);
|
||||||
|
Assert.Single(thirdSegment.Value.Matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TreeRouter_BuildDoesNotAddIntermediateMatchingNodes_ForRoutesWithIntermediateParametersWithConstrainedOptionalValues()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var builder = CreateBuilder();
|
||||||
|
|
||||||
|
builder.MapInbound(
|
||||||
|
Mock.Of<IRouter>(),
|
||||||
|
TemplateParser.Parse("a/{b:int?}/c"),
|
||||||
|
"Intermediate",
|
||||||
|
order: 0);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tree = builder.Build();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(tree);
|
||||||
|
Assert.NotNull(tree.MatchingTrees);
|
||||||
|
var matchingTree = Assert.Single(tree.MatchingTrees);
|
||||||
|
|
||||||
|
var firstSegment = Assert.Single(matchingTree.Root.Literals);
|
||||||
|
Assert.Equal("a", firstSegment.Key);
|
||||||
|
Assert.NotNull(firstSegment.Value.ConstrainedParameters);
|
||||||
|
|
||||||
|
var secondSegment = firstSegment.Value.ConstrainedParameters;
|
||||||
|
Assert.Empty(secondSegment.Matches);
|
||||||
|
|
||||||
|
var thirdSegment = Assert.Single(secondSegment.Literals);
|
||||||
|
Assert.Equal("c", thirdSegment.Key);
|
||||||
|
Assert.Single(thirdSegment.Value.Matches);
|
||||||
|
}
|
||||||
|
|
||||||
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(UrlEncoder.Default);
|
||||||
var objectPool = objectPoolProvider.Create<UriBuildingContext>(objectPolicy);
|
var objectPool = objectPoolProvider.Create(objectPolicy);
|
||||||
|
|
||||||
var constraintResolver = Mock.Of<IInlineConstraintResolver>();
|
var constraintResolver = GetInlineConstraintResolver();
|
||||||
var builder = new TreeRouteBuilder(
|
var builder = new TreeRouteBuilder(
|
||||||
NullLoggerFactory.Instance,
|
NullLoggerFactory.Instance,
|
||||||
UrlEncoder.Default,
|
UrlEncoder.Default,
|
||||||
|
|
@ -84,5 +259,13 @@ namespace Microsoft.AspNetCore.Routing.Tree
|
||||||
constraintResolver);
|
constraintResolver);
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IInlineConstraintResolver GetInlineConstraintResolver()
|
||||||
|
{
|
||||||
|
var services = new ServiceCollection().AddOptions();
|
||||||
|
var serviceProvider = services.BuildServiceProvider();
|
||||||
|
var accessor = serviceProvider.GetRequiredService<IOptions<RouteOptions>>();
|
||||||
|
return new DefaultInlineConstraintResolver(accessor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ using Microsoft.Extensions.Options;
|
||||||
using Microsoft.Extensions.WebEncoders.Testing;
|
using Microsoft.Extensions.WebEncoders.Testing;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Extensions;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Routing.Tree
|
namespace Microsoft.AspNetCore.Routing.Tree
|
||||||
{
|
{
|
||||||
|
|
@ -274,6 +273,71 @@ namespace Microsoft.AspNetCore.Routing.Tree
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TreeRouter_RouteAsync_DoesNotMatchRoutesWithIntermediateDefaultRouteValues()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var url = "/a/b";
|
||||||
|
|
||||||
|
var builder = CreateBuilder();
|
||||||
|
|
||||||
|
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("a/{b=3}/c/{d?}/e/{*f}", "/a")]
|
||||||
|
[InlineData("a/{b=3}/c/{d?}/e/{*f}", "/a/b")]
|
||||||
|
[InlineData("a/{b=3}/c/{d?}/e/{*f}", "/a/b/c")]
|
||||||
|
[InlineData("a/{b=3}/c/{d?}/e/{*f}", "/a/b/c/d")]
|
||||||
|
public async Task TreeRouter_RouteAsync_DoesNotMatchRoutesWithMultipleIntermediateDefaultOrOptionalRouteValues(string template, string url)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var builder = CreateBuilder();
|
||||||
|
|
||||||
|
MapInboundEntry(builder, template);
|
||||||
|
|
||||||
|
var route = builder.Build();
|
||||||
|
|
||||||
|
var context = CreateRouteContext(url);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await route.RouteAsync(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(context.Handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("a/{b=3}/c/{d?}/e/{*f}", "/a/b/c/d/e")]
|
||||||
|
[InlineData("a/{b=3}/c/{d?}/e/{*f}", "/a/b/c/d/e/f")]
|
||||||
|
public async Task RouteAsync_MatchRoutesWithMultipleIntermediateDefaultOrOptionalRouteValues_WhenAllIntermediateValuesAreProvided(string template, string url)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var builder = CreateBuilder();
|
||||||
|
|
||||||
|
MapInboundEntry(builder, template);
|
||||||
|
|
||||||
|
var route = builder.Build();
|
||||||
|
|
||||||
|
var context = CreateRouteContext(url);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await route.RouteAsync(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(context.Handler);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task TreeRouter_RouteAsync_DoesNotMatchShorterUrl()
|
public async Task TreeRouter_RouteAsync_DoesNotMatchShorterUrl()
|
||||||
{
|
{
|
||||||
|
|
@ -1032,6 +1096,27 @@ namespace Microsoft.AspNetCore.Routing.Tree
|
||||||
Assert.Empty(result.DataTokens);
|
Assert.Empty(result.DataTokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TreeRouter_GenerateLink_CreatesLinksForRoutesWithIntermediateDefaultRouteValues()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var builder = CreateBuilder();
|
||||||
|
|
||||||
|
MapOutboundEntry(builder, template: "a/b/{parameter3=3}/d", requiredValues: null, order: 0);
|
||||||
|
|
||||||
|
var route = builder.Build();
|
||||||
|
|
||||||
|
var context = CreateVirtualPathContext(values: null, ambientValues: null);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = route.GetVirtualPath(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.Equal("/a/b/3/d", result.VirtualPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TreeRouter_GeneratesLink_ForMultipleNamedEntriesWithTheSameTemplate()
|
public void TreeRouter_GeneratesLink_ForMultipleNamedEntriesWithTheSameTemplate()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"buildOptions": {
|
"buildOptions": {
|
||||||
"warningsAsErrors": true
|
"warningsAsErrors": true,
|
||||||
|
"keyFile": "../../tools/Key.snk"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dotnet-test-xunit": "2.2.0-*",
|
"dotnet-test-xunit": "2.2.0-*",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue