Skip to content

Commit d296c4d

Browse files
Chris Martinezcommonsensesoftware
Chris Martinez
authored andcommitted
Update action selection for routes with overlapping route templates. Fixes #148. Relates to #147.
1 parent 0bbf8da commit d296c4d

File tree

4 files changed

+72
-4
lines changed

4 files changed

+72
-4
lines changed

src/Microsoft.AspNetCore.Mvc.Versioning/Microsoft.AspNetCore.Mvc.Versioning.csproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<VersionPrefix>1.1.0</VersionPrefix>
4+
<VersionPrefix>1.1.1</VersionPrefix>
55
<AssemblyVersion>1.1.0.0</AssemblyVersion>
66
<TargetFrameworks>net451;netstandard1.6</TargetFrameworks>
77
<NetStandardImplicitPackageVersion>1.6.1</NetStandardImplicitPackageVersion>
@@ -11,7 +11,7 @@
1111
<Description>A service API versioning library for Microsoft ASP.NET Core.</Description>
1212
<RootNamespace>Microsoft.AspNetCore.Mvc</RootNamespace>
1313
<PackageTags>Microsoft;AspNet;AspNetCore;Versioning</PackageTags>
14-
<PackageReleaseNotes>https://github.com/Microsoft/aspnet-api-versioning/releases/tag/v1.1.0</PackageReleaseNotes>
14+
<PackageReleaseNotes>• Fixed overlapped route action selection (Issue #148)</PackageReleaseNotes>
1515
</PropertyGroup>
1616

1717
<ItemGroup>

src/Microsoft.AspNetCore.Mvc.Versioning/Versioning/ApiVersionActionSelector.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ public virtual ActionDescriptor SelectBestCandidate( RouteContext context, IRead
120120
var selectedAction = finalMatches[0];
121121

122122
// note: short-circuit if the api version policy has already been applied to the match
123-
if ( selectedAction.VersionPolicyIsApplied() )
123+
// and no other better match has already been selected
124+
if ( selectedAction.VersionPolicyIsApplied() && selectionResult.BestMatch == null )
124125
{
125126
httpContext.ApiVersionProperties().ApiVersion = selectionContext.RequestedVersion;
126127
return selectedAction;
@@ -137,7 +138,7 @@ public virtual ActionDescriptor SelectBestCandidate( RouteContext context, IRead
137138
}
138139

139140
// note: even though we may have had a successful match, this method could be called multiple times. the final decision
140-
// is made by the IApiVersionRoutePolicy. we return here to make sure all candidates have been considered.
141+
// is made by the IApiVersionRoutePolicy. we return here to make sure all candidates have been considered at least once.
141142
selectionResult.EndIteration();
142143
return null;
143144
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
namespace Microsoft.AspNetCore.Mvc.Basic.Controllers
2+
{
3+
using Microsoft.AspNetCore.Mvc;
4+
using System;
5+
6+
[ApiVersion( "1.0" )]
7+
[Route( "api/v{version:apiVersion}/values" )]
8+
public class OverlappingRouteTemplateController : Controller
9+
{
10+
[HttpGet( "{id:int}/{childId}" )]
11+
public IActionResult Get( int id, string childId ) => Ok( new { id, childId } );
12+
13+
[HttpGet( "{id:int}/children" )]
14+
public IActionResult Get( int id ) => Ok( new { id } );
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
namespace given_a_versioned_Controller
2+
{
3+
using FluentAssertions;
4+
using Microsoft.AspNetCore.Mvc;
5+
using Microsoft.AspNetCore.Mvc.Basic;
6+
using Microsoft.AspNetCore.Mvc.Basic.Controllers;
7+
using System.Reflection;
8+
using System.Threading.Tasks;
9+
using Xunit;
10+
11+
public class when_two_route_templates_overlap : BasicAcceptanceTest
12+
{
13+
public when_two_route_templates_overlap()
14+
{
15+
FilteredControllerTypes.Clear();
16+
FilteredControllerTypes.Add( typeof( OverlappingRouteTemplateController ).GetTypeInfo() );
17+
}
18+
19+
[Fact]
20+
public async Task then_the_higher_precedence_route_should_be_selected_during_the_first_request()
21+
{
22+
// arrange
23+
var response = await Client.GetAsync( "api/v1/values/42/children" );
24+
var result1 = await response.EnsureSuccessStatusCode().Content.ReadAsStringAsync();
25+
26+
// act
27+
response = await Client.GetAsync( "api/v1/values/42/abc" );
28+
var result2 = await response.EnsureSuccessStatusCode().Content.ReadAsStringAsync();
29+
30+
// assert
31+
result1.Should().Be( "{\"id\":42}" );
32+
result2.Should().Be( "{\"id\":42,\"childId\":\"abc\"}" );
33+
}
34+
35+
[Fact]
36+
public async Task then_the_higher_precedence_route_should_be_selected_during_the_second_request()
37+
{
38+
// arrange
39+
var response = await Client.GetAsync( "api/v1/values/42/abc" );
40+
var result1 = await response.EnsureSuccessStatusCode().Content.ReadAsStringAsync();
41+
42+
// act
43+
response = await Client.GetAsync( "api/v1/values/42/children" );
44+
var result2 = await response.EnsureSuccessStatusCode().Content.ReadAsStringAsync();
45+
46+
// assert
47+
result1.Should().Be( "{\"id\":42,\"childId\":\"abc\"}" );
48+
result2.Should().Be( "{\"id\":42}" );
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)