Skip to content

Commit e8f04d0

Browse files
Chris Martinezcommonsensesoftware
authored andcommitted
Defer reporting API version response headers
1 parent 2a74b93 commit e8f04d0

File tree

2 files changed

+63
-32
lines changed

2 files changed

+63
-32
lines changed

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
namespace Microsoft.AspNetCore.Mvc
22
{
3+
using Microsoft.AspNetCore.Http;
34
using Microsoft.AspNetCore.Mvc.Abstractions;
45
using Microsoft.AspNetCore.Mvc.Filters;
56
using Microsoft.AspNetCore.Mvc.Versioning;
67
using System;
8+
using System.Threading.Tasks;
9+
using static System.Threading.Tasks.Task;
710

811
/// <content>
912
/// Provides additional implementation specific to ASP.NET Core.
@@ -49,8 +52,15 @@ public override void OnActionExecuting( ActionExecutingContext context )
4952

5053
if ( !model.IsApiVersionNeutral )
5154
{
52-
reporter.Report( response.Headers, model );
55+
response.OnStarting( ReportApiVersions, (response.Headers, model) );
5356
}
5457
}
58+
59+
Task ReportApiVersions( object state )
60+
{
61+
var (headers, model) = ((IHeaderDictionary, ApiVersionModel)) state;
62+
reporter.Report( headers, model );
63+
return CompletedTask;
64+
}
5565
}
5666
}
Lines changed: 52 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,89 @@
11
namespace Microsoft.AspNetCore.Mvc
22
{
3-
using Abstractions;
4-
using AspNetCore.Routing;
5-
using Filters;
63
using FluentAssertions;
7-
using Http;
4+
using Microsoft.AspNetCore.Http;
5+
using Microsoft.AspNetCore.Mvc.Abstractions;
6+
using Microsoft.AspNetCore.Mvc.Filters;
7+
using Microsoft.AspNetCore.Mvc.Versioning;
8+
using Microsoft.AspNetCore.Routing;
89
using Moq;
10+
using System;
911
using System.Collections.Generic;
1012
using System.Linq;
11-
using Versioning;
13+
using System.Threading.Tasks;
1214
using Xunit;
1315

1416
public class ReportApiVersionsAttributeTest
1517
{
16-
static ActionExecutingContext CreateContext( ApiVersionModel model )
17-
{
18-
var headers = new HeaderDictionary();
19-
var response = new Mock<HttpResponse>();
20-
var httpContext = new Mock<HttpContext>();
21-
var action = new ActionDescriptor();
22-
var actionContext = new ActionContext( httpContext.Object, new RouteData(), action );
23-
var filters = new IFilterMetadata[0];
24-
var actionArguments = new Dictionary<string, object>();
25-
var controller = default( object );
26-
27-
response.SetupGet( r => r.Headers ).Returns( headers );
28-
httpContext.SetupGet( c => c.Response ).Returns( response.Object );
29-
action.SetProperty( model );
30-
31-
return new ActionExecutingContext( actionContext, filters, actionArguments, controller );
32-
}
33-
3418
[Fact]
35-
public void on_action_executing_should_add_version_headers()
19+
public async Task on_action_executing_should_add_version_headers()
3620
{
3721
// arrange
3822
var supported = new[] { new ApiVersion( 1, 0 ), new ApiVersion( 2, 0 ) };
3923
var deprecated = new[] { new ApiVersion( 0, 5 ) };
4024
var model = new ApiVersionModel( supported, deprecated );
41-
var context = CreateContext( model );
25+
var onStartResponse = new List<(Func<object, Task>, object)>();
26+
var context = CreateContext( model, onStartResponse );
4227
var attribute = new ReportApiVersionsAttribute();
4328

4429
// act
4530
attribute.OnActionExecuting( context );
4631

32+
for ( var i = 0; i < onStartResponse.Count; i++ )
33+
{
34+
var (callback, state) = onStartResponse[i];
35+
await callback( state );
36+
}
37+
4738
// assert
48-
context.HttpContext.Response.Headers["api-supported-versions"].Single().Should().Be( "1.0, 2.0" );
49-
context.HttpContext.Response.Headers["api-deprecated-versions"].Single().Should().Be( "0.5" );
39+
var headers = context.HttpContext.Response.Headers;
40+
41+
headers["api-supported-versions"].Single().Should().Be( "1.0, 2.0" );
42+
headers["api-deprecated-versions"].Single().Should().Be( "0.5" );
5043
}
5144

5245
[Fact]
53-
public void on_action_executing_should_not_add_headers_for_versionX2Dneutral_controller()
46+
public async Task on_action_executing_should_not_add_headers_for_versionX2Dneutral_controller()
5447
{
5548
// arrange
56-
var context = CreateContext( ApiVersionModel.Neutral );
49+
var onStartResponse = new List<(Func<object, Task>, object)>();
50+
var context = CreateContext( ApiVersionModel.Neutral, onStartResponse );
5751
var attribute = new ReportApiVersionsAttribute();
5852

5953
// act
6054
attribute.OnActionExecuting( context );
6155

56+
for ( var i = 0; i < onStartResponse.Count; i++ )
57+
{
58+
var (callback, state) = onStartResponse[i];
59+
await callback( state );
60+
}
6261

6362
// assert
64-
context.HttpContext.Response.Headers.ContainsKey( "api-supported-versions" ).Should().BeFalse();
65-
context.HttpContext.Response.Headers.ContainsKey( "api-deprecated-versions" ).Should().BeFalse();
63+
var headers = context.HttpContext.Response.Headers;
64+
65+
headers.ContainsKey( "api-supported-versions" ).Should().BeFalse();
66+
headers.ContainsKey( "api-deprecated-versions" ).Should().BeFalse();
67+
}
68+
69+
static ActionExecutingContext CreateContext( ApiVersionModel model, ICollection<(Func<object, Task>, object)> onStartResponse )
70+
{
71+
var headers = new HeaderDictionary();
72+
var response = new Mock<HttpResponse>();
73+
var httpContext = new Mock<HttpContext>();
74+
var action = new ActionDescriptor();
75+
var actionContext = new ActionContext( httpContext.Object, new RouteData(), action );
76+
var filters = Array.Empty<IFilterMetadata>();
77+
var actionArguments = new Dictionary<string, object>();
78+
var controller = default( object );
79+
80+
response.SetupGet( r => r.Headers ).Returns( headers );
81+
response.Setup( r => r.OnStarting( It.IsAny<Func<object, Task>>(), It.IsAny<object>() ) )
82+
.Callback( ( Func<object, Task> callback, object state ) => onStartResponse.Add( (callback, state) ) );
83+
httpContext.SetupGet( c => c.Response ).Returns( response.Object );
84+
action.SetProperty( model );
85+
86+
return new ActionExecutingContext( actionContext, filters, actionArguments, controller );
6687
}
6788
}
6889
}

0 commit comments

Comments
 (0)