diff --git a/src/Microsoft.AspNetCore.Routing/Matchers/TreeMatcher.cs b/src/Microsoft.AspNetCore.Routing/Matchers/TreeMatcher.cs index a0970120eb..26923cd1c9 100644 --- a/src/Microsoft.AspNetCore.Routing/Matchers/TreeMatcher.cs +++ b/src/Microsoft.AspNetCore.Routing/Matchers/TreeMatcher.cs @@ -194,7 +194,7 @@ namespace Microsoft.AspNetCore.Routing.Matchers foreach (var group in groups) { var template = TemplateParser.Parse(group.Key.Template); - var entryExists = entries.Any(item => item.RouteTemplate.TemplateText == template.TemplateText); + var entryExists = entries.Any(item => item.RouteTemplate.TemplateText == template.TemplateText && item.Order == group.Key.Order); if (!entryExists) { entries.Add(MapInbound(template, group.Value.ToArray(), group.Key.Order)); diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matchers/TreeMatcherTests.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matchers/TreeMatcherTests.cs new file mode 100644 index 0000000000..68072aaaea --- /dev/null +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matchers/TreeMatcherTests.cs @@ -0,0 +1,57 @@ +// 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 Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNetCore.Routing.Matchers +{ + public class TreeMatcherTests + { + private MatcherEndpoint CreateEndpoint(string template, int order, object values = null) + { + return new MatcherEndpoint((next) => null, template, values, order, EndpointMetadataCollection.Empty, template); + } + + private TreeMatcher CreateTreeMatcher(EndpointDataSource endpointDataSource) + { + var defaultInlineConstraintResolver = new DefaultInlineConstraintResolver(Options.Create(new RouteOptions())); + return new TreeMatcher(defaultInlineConstraintResolver, NullLogger.Instance, endpointDataSource); + } + + [Fact] + public async Task MatchAsync_DuplicateTemplatesAndDifferentOrder_LowerOrderEndpointMatched() + { + // Arrange + var defaultInlineConstraintResolver = new DefaultInlineConstraintResolver(Options.Create(new RouteOptions())); + + var higherOrderEndpoint = CreateEndpoint("/Teams", 1); + var lowerOrderEndpoint = CreateEndpoint("/Teams", 0); + + var endpointDataSource = new DefaultEndpointDataSource(new List + { + higherOrderEndpoint, + lowerOrderEndpoint + }); + + var treeMatcher = CreateTreeMatcher(endpointDataSource); + + var httpContext = new DefaultHttpContext(); + httpContext.Request.Path = "/Teams"; + + var endpointFeature = new EndpointFeature(); + + // Act + await treeMatcher.MatchAsync(httpContext, endpointFeature); + + // Assert + Assert.Equal(lowerOrderEndpoint, endpointFeature.Endpoint); + } + } +}