Skip to content

Commit 44811d4

Browse files
Fixed the reporting of API versions when conventions are used and added additional test coverage. Fixes #47. (#50)
1 parent 664d76a commit 44811d4

File tree

8 files changed

+144
-43
lines changed

8 files changed

+144
-43
lines changed

src/Microsoft.AspNet.WebApi.Versioning/Versioning/Conventions/ControllerApiVersionConventionBuilderT.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ private void MergeAttributesWithConventions( HttpControllerDescriptor controller
4949

5050
var providers = controllerDescriptor.GetCustomAttributes<IApiVersionProvider>().ToArray();
5151

52+
if ( providers.Length == 0 )
53+
{
54+
return;
55+
}
56+
5257
supportedVersions.UnionWith( from provider in providers
5358
where !provider.AdvertiseOnly && !provider.Deprecated
5459
from version in provider.Versions

src/Microsoft.AspNetCore.Mvc.Versioning/Versioning/Conventions/ControllerApiVersionConventionBuilderT.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ private ControllerVersionInfo ApplyControllerConventions( ControllerModel contro
4343

4444
controllerModel.SetProperty( model );
4545

46-
return new ControllerVersionInfo( supportedVersions, deprecatedAdvertisedVersions, advertisedVersions, deprecatedAdvertisedVersions );
46+
return new ControllerVersionInfo( supportedVersions, deprecatedVersions, advertisedVersions, deprecatedAdvertisedVersions );
4747
}
4848

4949
private void MergeAttributesWithConventions( ControllerModel controllerModel )
@@ -57,6 +57,11 @@ private void MergeAttributesWithConventions( ControllerModel controllerModel )
5757

5858
var providers = controllerModel.Attributes.OfType<IApiVersionProvider>().ToArray();
5959

60+
if ( providers.Length == 0 )
61+
{
62+
return;
63+
}
64+
6065
supportedVersions.UnionWith( from provider in providers
6166
where !provider.AdvertiseOnly && !provider.Deprecated
6267
from version in provider.Versions
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace Microsoft.Web.Http.Conventions.Controllers
2+
{
3+
using System.Web.Http;
4+
5+
[RoutePrefix( "api/v{version:apiVersion}/helloworld" )]
6+
public class HelloWorld2Controller : ApiController
7+
{
8+
[Route]
9+
public IHttpActionResult Get() => Ok( new { controller = GetType().Name, version = Request.GetRequestedApiVersion().ToString() } );
10+
11+
[Route( "{id:int}" )]
12+
public IHttpActionResult Get( int id ) => Ok( new { controller = GetType().Name, id = id, version = Request.GetRequestedApiVersion().ToString() } );
13+
14+
[Route]
15+
public IHttpActionResult GetV3() => Ok( new { controller = GetType().Name, version = Request.GetRequestedApiVersion().ToString() } );
16+
17+
[Route( "{id:int}" )]
18+
public IHttpActionResult GetV3( int id ) => Ok( new { controller = GetType().Name, id = id, version = Request.GetRequestedApiVersion().ToString() } );
19+
}
20+
}

test/Microsoft.AspNet.WebApi.Acceptance.Tests/Http/Conventions/ConventionsAcceptanceTest.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ protected ConventionsAcceptanceTest()
1818
FilteredControllerTypes.Add( typeof( ValuesController ) );
1919
FilteredControllerTypes.Add( typeof( Values2Controller ) );
2020
FilteredControllerTypes.Add( typeof( HelloWorldController ) );
21+
FilteredControllerTypes.Add( typeof( HelloWorld2Controller ) );
2122
Configuration.AddApiVersioning(
2223
options =>
2324
{
@@ -28,10 +29,13 @@ protected ConventionsAcceptanceTest()
2829
.HasApiVersion( 3, 0 )
2930
.Action( c => c.GetV3() ).MapToApiVersion( 3, 0 )
3031
.Action( c => c.GetV3( default( int ) ) ).MapToApiVersion( 3, 0 );
31-
options.Conventions.Controller<HelloWorldController>()
32-
.HasApiVersion( 1, 0 )
32+
options.Conventions.Controller<HelloWorldController>().HasDeprecatedApiVersion( 1, 0 );
33+
options.Conventions.Controller<HelloWorld2Controller>()
3334
.HasApiVersion( 2, 0 )
34-
.AdvertisesApiVersion( 3, 0 );
35+
.HasApiVersion( 3, 0 )
36+
.AdvertisesApiVersion( 4, 0 )
37+
.Action( c => c.GetV3() ).MapToApiVersion( 3, 0 )
38+
.Action( c => c.GetV3( default( int ) ) ).MapToApiVersion( 3, 0 );
3539
} );
3640
Configuration.MapHttpAttributeRoutes( constraintResolver );
3741
Configuration.EnsureInitialized();

test/Microsoft.AspNet.WebApi.Acceptance.Tests/Http/Conventions/given/a_url_versioned_ApiController_using_conventions.cs

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,41 @@
1414
public class _a_url_versioned_ApiController_using_conventions : ConventionsAcceptanceTest
1515
{
1616
[Theory]
17-
[InlineData( "api/v1/helloworld", "1", null )]
18-
[InlineData( "api/v2/helloworld", "2", null )]
19-
[InlineData( "api/v1/helloworld/42", "1", "42" )]
20-
[InlineData( "api/v2/helloworld/42", "2", "42" )]
21-
public async Task _get_should_return_200( string requestUrl, string apiVersion, string id )
17+
[InlineData( "api/v1/helloworld", nameof( HelloWorldController ), "1" )]
18+
[InlineData( "api/v2/helloworld", nameof( HelloWorld2Controller ), "2" )]
19+
[InlineData( "api/v3/helloworld", nameof( HelloWorld2Controller ), "3" )]
20+
public async Task _get_should_return_200( string requestUrl, string controllerName, string apiVersion )
2221
{
2322
// arrange
24-
var body = new Dictionary<string, string>()
25-
{
26-
["controller"] = nameof( HelloWorldController ),
27-
["version"] = apiVersion
28-
};
23+
var example = new { controller = "", version = "" };
2924

30-
if ( !string.IsNullOrEmpty( id ) )
31-
{
32-
body["id"] = id;
33-
}
25+
// act
26+
var response = await GetAsync( requestUrl ).EnsureSuccessStatusCode();
27+
var content = await response.Content.ReadAsExampleAsync( example );
28+
29+
// assert
30+
response.Headers.GetValues( "api-supported-versions" ).Single().Should().Be( "2.0, 3.0, 4.0" );
31+
response.Headers.GetValues( "api-deprecated-versions" ).Single().Should().Be( "1.0" );
32+
content.ShouldBeEquivalentTo( new { controller = controllerName, version = apiVersion } );
33+
}
34+
35+
[Theory]
36+
[InlineData( "api/v1/helloworld/42", nameof( HelloWorldController ), "1" )]
37+
[InlineData( "api/v2/helloworld/42", nameof( HelloWorld2Controller ), "2" )]
38+
[InlineData( "api/v3/helloworld/42", nameof( HelloWorld2Controller ), "3" )]
39+
public async Task _get_with_id_should_return_200( string requestUrl, string controllerName, string apiVersion )
40+
{
41+
// arrange
42+
var example = new { controller = "", version = "", id = "" };
3443

3544
// act
3645
var response = await GetAsync( requestUrl ).EnsureSuccessStatusCode();
37-
var content = await response.Content.ReadAsAsync<IDictionary<string, string>>();
46+
var content = await response.Content.ReadAsExampleAsync( example );
3847

3948
// assert
40-
response.Headers.GetValues( "api-supported-versions" ).Single().Should().Be( "1.0, 2.0, 3.0" );
41-
content.ShouldBeEquivalentTo( body );
49+
response.Headers.GetValues( "api-supported-versions" ).Single().Should().Be( "2.0, 3.0, 4.0" );
50+
response.Headers.GetValues( "api-deprecated-versions" ).Single().Should().Be( "1.0" );
51+
content.ShouldBeEquivalentTo( new { controller = controllerName, version = apiVersion, id = "42" } );
4252
}
4353

4454
[Fact]
@@ -48,7 +58,7 @@ public async Task _get_should_return_400_when_version_is_unsupported()
4858

4959

5060
// act
51-
var response = await GetAsync( "api/v3/helloworld" );
61+
var response = await GetAsync( "api/v4/helloworld" );
5262
var content = await response.Content.ReadAsAsync<OneApiErrorResponse>();
5363

5464
// assert
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace Microsoft.AspNetCore.Mvc.Conventions.Controllers
2+
{
3+
using AspNetCore.Routing;
4+
using Microsoft.AspNetCore.Mvc;
5+
using System;
6+
7+
[Route( "api/v{version:apiVersion}/helloworld" )]
8+
public class HelloWorld2Controller : Controller
9+
{
10+
[HttpGet]
11+
public IActionResult Get() => Ok( new { Controller = nameof( HelloWorld2Controller ), Version = HttpContext.GetRequestedApiVersion().ToString() } );
12+
13+
[HttpGet( "{id:int}" )]
14+
public IActionResult Get( int id ) => Ok( new { Controller = nameof( HelloWorld2Controller ), Id = id, Version = HttpContext.GetRequestedApiVersion().ToString() } );
15+
16+
[HttpGet]
17+
public IActionResult GetV3() => Ok( new { Controller = nameof( HelloWorld2Controller ), Version = HttpContext.GetRequestedApiVersion().ToString() } );
18+
19+
[HttpGet( "{id:int}" )]
20+
public IActionResult GetV3( int id ) => Ok( new { Controller = nameof( HelloWorld2Controller ), Id = id, Version = HttpContext.GetRequestedApiVersion().ToString() } );
21+
}
22+
}

test/Microsoft.AspNetCore.Mvc.Acceptance.Tests/Conventions/ConventionsAcceptanceTest.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ protected ConventionsAcceptanceTest()
1212
FilteredControllerTypes.Add( typeof( ValuesController ).GetTypeInfo() );
1313
FilteredControllerTypes.Add( typeof( Values2Controller ).GetTypeInfo() );
1414
FilteredControllerTypes.Add( typeof( HelloWorldController ).GetTypeInfo() );
15+
FilteredControllerTypes.Add( typeof( HelloWorld2Controller ).GetTypeInfo() );
1516
}
1617

1718
protected override void OnAddApiVersioning( ApiVersioningOptions options )
@@ -23,10 +24,13 @@ protected override void OnAddApiVersioning( ApiVersioningOptions options )
2324
.HasApiVersion( 3, 0 )
2425
.Action( c => c.GetV3() ).MapToApiVersion( 3, 0 )
2526
.Action( c => c.GetV3( default( int ) ) ).MapToApiVersion( 3, 0 );
26-
options.Conventions.Controller<HelloWorldController>()
27-
.HasApiVersion( 1, 0 )
27+
options.Conventions.Controller<HelloWorldController>().HasDeprecatedApiVersion( 1, 0 );
28+
options.Conventions.Controller<HelloWorld2Controller>()
2829
.HasApiVersion( 2, 0 )
29-
.AdvertisesApiVersion( 3, 0 );
30+
.HasApiVersion( 3, 0 )
31+
.AdvertisesApiVersion( 4, 0 )
32+
.Action( c => c.GetV3() ).MapToApiVersion( 3, 0 )
33+
.Action( c => c.GetV3( default( int ) ) ).MapToApiVersion( 3, 0 );
3034
}
3135
}
3236
}

test/Microsoft.AspNetCore.Mvc.Acceptance.Tests/Conventions/given/a_url_versioned_Controller_using_conventions.cs

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,63 @@
1313

1414
public class _a_url_versioned_Controller_using_conventions : ConventionsAcceptanceTest
1515
{
16-
[Theory]
17-
[InlineData( "api/v1/helloworld", "1", null )]
18-
[InlineData( "api/v2/helloworld", "2", null )]
19-
[InlineData( "api/v1/helloworld/42", "1", "42" )]
20-
[InlineData( "api/v2/helloworld/42", "2", "42" )]
21-
public async Task _get_should_return_200( string requestUrl, string apiVersion, string id )
16+
[Fact]
17+
public async Task _get_should_return_200()
2218
{
19+
// REMARKS: this test should be a theory, but when it is, it becomes flaky. any failure succeeds when run again.
20+
// the exact cause is unknown, but seems to be related to some form of caching. running a loop in a single test
21+
// case seems to resolve the problem.
22+
2323
// arrange
24-
var body = new Dictionary<string, string>()
24+
var iterations = new[]
2525
{
26-
["controller"] = nameof( HelloWorldController ),
27-
["version"] = apiVersion
26+
new { RequestUrl = "api/v1/helloworld", ControllerName = nameof( HelloWorldController ), ApiVersion = "1" },
27+
new { RequestUrl = "api/v2/helloworld", ControllerName = nameof( HelloWorld2Controller ), ApiVersion = "2" },
28+
new { RequestUrl = "api/v3/helloworld", ControllerName = nameof( HelloWorld2Controller ), ApiVersion = "3" },
2829
};
30+
var example = new { controller = "", version = "" };
2931

30-
if ( !string.IsNullOrEmpty( id ) )
32+
foreach ( var iteration in iterations )
3133
{
32-
body["id"] = id;
34+
// act
35+
var response = await GetAsync( iteration.RequestUrl ).EnsureSuccessStatusCode();
36+
var content = await response.Content.ReadAsExampleAsync( example );
37+
38+
// assert
39+
response.Headers.GetValues( "api-supported-versions" ).Single().Should().Be( "2.0, 3.0, 4.0" );
40+
response.Headers.GetValues( "api-deprecated-versions" ).Single().Should().Be( "1.0" );
41+
content.ShouldBeEquivalentTo( new { controller = iteration.ControllerName, version = iteration.ApiVersion } );
3342
}
43+
}
3444

35-
// act
36-
var response = await GetAsync( requestUrl ).EnsureSuccessStatusCode();
37-
var content = await response.Content.ReadAsAsync<IDictionary<string, string>>();
45+
[Fact]
46+
public async Task _get_with_id_should_return_200()
47+
{
48+
// REMARKS: this test should be a theory, but when it is, it becomes flaky. any failure succeeds when run again.
49+
// the exact cause is unknown, but seems to be related to some form of caching. running a loop in a single test
50+
// case seems to resolve the problem.
3851

39-
// assert
40-
response.Headers.GetValues( "api-supported-versions" ).Single().Should().Be( "1.0, 2.0, 3.0" );
41-
content.ShouldBeEquivalentTo( body );
52+
// arrange
53+
var iterations = new[]
54+
{
55+
new { RequestUrl = "api/v1/helloworld/42", ControllerName = nameof( HelloWorldController ), ApiVersion = "1" },
56+
new { RequestUrl = "api/v2/helloworld/42", ControllerName = nameof( HelloWorld2Controller ), ApiVersion = "2" },
57+
new { RequestUrl = "api/v3/helloworld/42", ControllerName = nameof( HelloWorld2Controller ), ApiVersion = "3" },
58+
};
59+
60+
var example = new { controller = "", version = "", id = "" };
61+
62+
foreach ( var iteration in iterations )
63+
{
64+
// act
65+
var response = await GetAsync( iteration.RequestUrl ).EnsureSuccessStatusCode();
66+
var content = await response.Content.ReadAsExampleAsync( example );
67+
68+
// assert
69+
response.Headers.GetValues( "api-supported-versions" ).Single().Should().Be( "2.0, 3.0, 4.0" );
70+
response.Headers.GetValues( "api-deprecated-versions" ).Single().Should().Be( "1.0" );
71+
content.ShouldBeEquivalentTo( new { controller = iteration.ControllerName, version = iteration.ApiVersion, id = "42" } );
72+
}
4273
}
4374

4475
[Fact]
@@ -48,7 +79,7 @@ public async Task _get_should_return_400_when_version_is_unsupported()
4879

4980

5081
// act
51-
var response = await GetAsync( "api/v3/helloworld" );
82+
var response = await GetAsync( "api/v4/helloworld" );
5283
var content = await response.Content.ReadAsAsync<OneApiErrorResponse>();
5384

5485
// assert

0 commit comments

Comments
 (0)