Skip to content

Commit 64e7bbb

Browse files
Refactor to more easily enable extensibility; addresses #87. (#90)
1 parent 94d7f8c commit 64e7bbb

File tree

19 files changed

+402
-413
lines changed

19 files changed

+402
-413
lines changed

src/Common/Versioning/ApiVersionModel.cs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,10 @@ internal ApiVersionModel(
9696
}
9797
else
9898
{
99-
declaredVersions = new Lazy<IReadOnlyList<ApiVersion>>( () => supported.Union( deprecated ).OrderBy( v => v ).ToArray() );
100-
supportedVersions = new Lazy<IReadOnlyList<ApiVersion>>( () => supported.Union( advertised ).OrderBy( v => v ).ToArray() );
101-
deprecatedVersions = new Lazy<IReadOnlyList<ApiVersion>>( () => deprecated.Union( deprecatedAdvertised ).OrderBy( v => v ).ToArray() );
102-
implementedVersions = new Lazy<IReadOnlyList<ApiVersion>>( () => supportedVersions.Value.Union( deprecatedVersions.Value ).OrderBy( v => v ).ToArray() );
99+
declaredVersions = new Lazy<IReadOnlyList<ApiVersion>>( supported.Union( deprecated ).ToSortedReadOnlyList );
100+
supportedVersions = new Lazy<IReadOnlyList<ApiVersion>>( supported.Union( advertised ).ToSortedReadOnlyList );
101+
deprecatedVersions = new Lazy<IReadOnlyList<ApiVersion>>( deprecated.Union( deprecatedAdvertised ).ToSortedReadOnlyList );
102+
implementedVersions = new Lazy<IReadOnlyList<ApiVersion>>( () => supportedVersions.Value.Union( deprecatedVersions.Value ).ToSortedReadOnlyList() );
103103
}
104104
}
105105

@@ -138,6 +138,33 @@ public ApiVersionModel( IEnumerable<ApiVersion> supportedVersions, IEnumerable<A
138138
this.deprecatedVersions = new Lazy<IReadOnlyList<ApiVersion>>( deprecatedVersions.ToSortedReadOnlyList );
139139
}
140140

141+
/// <summary>
142+
/// Initializes a new instance of the <see cref="ApiVersionModel"/> class.
143+
/// </summary>
144+
/// <param name="declaredVersions">The declared <see cref="IEnumerable{T}">sequence</see> of <see cref="ApiVersion">API versions</see> on a controller or action.</param>
145+
/// <param name="supportedVersions">The supported <see cref="IEnumerable{T}">sequence</see> of <see cref="ApiVersion">API versions</see> on a controller.</param>
146+
/// <param name="deprecatedVersions">The deprecated <see cref="IEnumerable{T}">sequence</see> of <see cref="ApiVersion">API versions</see> on a controller.</param>
147+
/// <param name="advertisedVersions">The advertised <see cref="IEnumerable{T}">sequence</see> of <see cref="ApiVersion">API versions</see> on a controller.</param>
148+
/// <param name="deprecatedAdvertisedVersions">The deprecated, advertised <see cref="IEnumerable{T}">sequence</see> of <see cref="ApiVersion">API versions</see> on a controller.</param>
149+
public ApiVersionModel(
150+
IEnumerable<ApiVersion> declaredVersions,
151+
IEnumerable<ApiVersion> supportedVersions,
152+
IEnumerable<ApiVersion> deprecatedVersions,
153+
IEnumerable<ApiVersion> advertisedVersions,
154+
IEnumerable<ApiVersion> deprecatedAdvertisedVersions )
155+
{
156+
Arg.NotNull( declaredVersions, nameof( declaredVersions ) );
157+
Arg.NotNull( supportedVersions, nameof( supportedVersions ) );
158+
Arg.NotNull( deprecatedVersions, nameof( deprecatedVersions ) );
159+
Arg.NotNull( advertisedVersions, nameof( advertisedVersions ) );
160+
Arg.NotNull( deprecatedAdvertisedVersions, nameof( deprecatedAdvertisedVersions ) );
161+
162+
this.declaredVersions = new Lazy<IReadOnlyList<ApiVersion>>( declaredVersions.ToSortedReadOnlyList );
163+
this.supportedVersions = new Lazy<IReadOnlyList<ApiVersion>>( supportedVersions.Union( advertisedVersions ).ToSortedReadOnlyList );
164+
this.deprecatedVersions = new Lazy<IReadOnlyList<ApiVersion>>( deprecatedVersions.Union( deprecatedAdvertisedVersions ).ToSortedReadOnlyList );
165+
this.implementedVersions = new Lazy<IReadOnlyList<ApiVersion>>( () => this.supportedVersions.Value.Union( this.deprecatedVersions.Value ).ToSortedReadOnlyList() );
166+
}
167+
141168
private string DebuggerDisplayText => IsApiVersionNeutral ? "*.*" : string.Join( ", ", DeclaredApiVersions );
142169

143170
/// <summary>
@@ -198,4 +225,4 @@ public ApiVersionModel( IEnumerable<ApiVersion> supportedVersions, IEnumerable<A
198225
/// controller would no longer indicate that it is an <see cref="ImplementedApiVersions">implemented version</see>.</remarks>
199226
public IReadOnlyList<ApiVersion> DeprecatedApiVersions => deprecatedVersions.Value;
200227
}
201-
}
228+
}

src/Microsoft.AspNet.WebApi.Versioning/Versioning/ApiVersionModel.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,13 @@ private ApiVersionModel()
2020
deprecatedVersions = emptyVersions;
2121
}
2222

23-
internal ApiVersionModel( HttpControllerDescriptor controllerDescriptor )
23+
/// <summary>
24+
/// Initializes a new instance of the <see cref="ApiVersionModel"/> class.
25+
/// </summary>
26+
/// <param name="controllerDescriptor">The <see cref="HttpControllerDescriptor"/> to initialize the API version model from.</param>
27+
public ApiVersionModel( HttpControllerDescriptor controllerDescriptor )
2428
{
25-
Contract.Requires( controllerDescriptor != null );
29+
Arg.NotNull( controllerDescriptor, nameof( controllerDescriptor ) );
2630

2731
if ( IsApiVersionNeutral = controllerDescriptor.GetCustomAttributes<IApiVersionNeutral>( false ).Any() )
2832
{
@@ -61,9 +65,13 @@ internal ApiVersionModel( HttpControllerDescriptor controllerDescriptor, ApiVers
6165
}
6266
}
6367

64-
internal ApiVersionModel( HttpActionDescriptor actionDescriptor )
68+
/// <summary>
69+
/// Initializes a new instance of the <see cref="ApiVersionModel"/> class.
70+
/// </summary>
71+
/// <param name="actionDescriptor">The <see cref="HttpActionDescriptor"/> to initialize the API version model from.</param>
72+
public ApiVersionModel( HttpActionDescriptor actionDescriptor )
6573
{
66-
Contract.Requires( actionDescriptor != null );
74+
Arg.NotNull( actionDescriptor, nameof( actionDescriptor ) );
6775

6876
if ( IsApiVersionNeutral = actionDescriptor.ControllerDescriptor.GetCustomAttributes<IApiVersionNeutral>( false ).Any() )
6977
{

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ public virtual ControllerApiVersionConventionBuilder<TController> Controller<TCo
5050
/// </summary>
5151
/// <param name="controllerDescriptor">The <see cref="HttpControllerDescriptor">controller descriptor</see>
5252
/// to apply configured conventions to.</param>
53-
public virtual void ApplyTo( HttpControllerDescriptor controllerDescriptor )
53+
/// <returns>True if any conventions were applied to the
54+
/// <paramref name="controllerDescriptor">controller descriptor</paramref>; otherwise, false.</returns>
55+
public virtual bool ApplyTo( HttpControllerDescriptor controllerDescriptor )
5456
{
5557
Arg.NotNull( controllerDescriptor, nameof( controllerDescriptor ) );
5658

@@ -60,7 +62,10 @@ public virtual void ApplyTo( HttpControllerDescriptor controllerDescriptor )
6062
if ( ControllerConventions.TryGetValue( key, out convention ) )
6163
{
6264
convention.ApplyTo( controllerDescriptor );
65+
return true;
6366
}
67+
68+
return false;
6469
}
6570
}
6671
}

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

Lines changed: 0 additions & 28 deletions
This file was deleted.

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

Lines changed: 0 additions & 29 deletions
This file was deleted.

src/Microsoft.AspNetCore.Mvc.Versioning/ApplicationModels/ModelExtensions.cs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,6 @@
1111
[CLSCompliant( false )]
1212
public static class ModelExtensions
1313
{
14-
/// <summary>
15-
/// Returns a value indicating whether controller model has explicit version information.
16-
/// </summary>
17-
/// <param name="controller">The <see cref="ControllerModel">model</see> to evaluate.</param>
18-
/// <returns>True if the <paramref name="controller"/> has explicit version information; otherwise, false.</returns>
19-
public static bool HasExplicitVersioning( this ControllerModel controller )
20-
{
21-
Arg.NotNull( controller, nameof( controller ) );
22-
return controller.Properties.ContainsKey( typeof( ApiVersionModel ) ) || controller.Attributes.OfType<IApiVersionProvider>().Any();
23-
}
24-
2514
/// <summary>
2615
/// Gets the property associated with the controller model.
2716
/// </summary>

src/Microsoft.AspNetCore.Mvc.Versioning/Microsoft.Extensions.DependencyInjection/IServiceCollectionExtensions.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,7 @@ public static IServiceCollection AddApiVersioning( this IServiceCollection servi
5555
mvcOptions.Filters.Add( typeof( ReportApiVersionsAttribute ) );
5656
}
5757

58-
if ( options.Conventions.Count > 0 )
59-
{
60-
mvcOptions.Conventions.Add( new ApiVersionConvention( options.Conventions ) );
61-
}
62-
63-
mvcOptions.Conventions.Add( new ImplicitControllerVersionConvention( options.DefaultApiVersion ) );
58+
mvcOptions.Conventions.Add( new ApiVersionConvention( options.DefaultApiVersion, options.Conventions ) );
6459
} );
6560

6661
services.AddRouting( mvcOptions => mvcOptions.ConstraintMap.Add( "apiVersion", typeof( ApiVersionRouteConstraint ) ) );

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

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using ApplicationModels;
44
using Conventions;
55
using System;
6+
using System.Diagnostics.Contracts;
67

78
/// <summary>
89
/// Represents an <see cref="IApplicationModelConvention">application model convention</see> which applies
@@ -12,15 +13,43 @@
1213
public class ApiVersionConvention : IApplicationModelConvention
1314
{
1415
private readonly ApiVersionConventionBuilder conventionBuilder;
16+
private readonly ApiVersionModel implicitVersionModel;
1517

1618
/// <summary>
1719
/// Initializes a new instance of the <see cref="ApiVersionConvention"/> class.
1820
/// </summary>
21+
public ApiVersionConvention()
22+
{
23+
implicitVersionModel = ApiVersionModel.Default;
24+
conventionBuilder = new ApiVersionConventionBuilder();
25+
}
26+
27+
/// <summary>
28+
/// Initializes a new instance of the <see cref="ApiVersionConvention"/> class.
29+
/// </summary>
30+
/// <param name="implicitlyDeclaredVersion">The implicitly declared <see cref="ApiVersion">API version</see> for
31+
/// controllers and actions that have no other API versioning information applied.</param>
32+
public ApiVersionConvention( ApiVersion implicitlyDeclaredVersion )
33+
{
34+
Arg.NotNull( implicitlyDeclaredVersion, nameof( implicitlyDeclaredVersion ) );
35+
36+
implicitVersionModel = new ApiVersionModel( implicitlyDeclaredVersion );
37+
conventionBuilder = new ApiVersionConventionBuilder();
38+
}
39+
40+
/// <summary>
41+
/// Initializes a new instance of the <see cref="ApiVersionConvention"/> class.
42+
/// </summary>
43+
/// <param name="implicitlyDeclaredVersion">The implicitly declared <see cref="ApiVersion">API version</see> for
44+
/// controllers and actions that have no other API versioning information applied.</param>
1945
/// <param name="conventionBuilder">The <see cref="ApiVersionConventionBuilder">convention builder</see>
20-
/// containing the configured conventions to aply.</param>
21-
public ApiVersionConvention( ApiVersionConventionBuilder conventionBuilder )
46+
/// containing the configured conventions to apply.</param>
47+
public ApiVersionConvention( ApiVersion implicitlyDeclaredVersion, ApiVersionConventionBuilder conventionBuilder )
2248
{
49+
Arg.NotNull( implicitlyDeclaredVersion, nameof( implicitlyDeclaredVersion ) );
2350
Arg.NotNull( conventionBuilder, nameof( conventionBuilder ) );
51+
52+
implicitVersionModel = new ApiVersionModel( implicitlyDeclaredVersion );
2453
this.conventionBuilder = conventionBuilder;
2554
}
2655

@@ -30,9 +59,64 @@ public ApiVersionConvention( ApiVersionConventionBuilder conventionBuilder )
3059
/// <param name="application">The <see cref="ApplicationModel">application</see> to apply the convention to.</param>
3160
public void Apply( ApplicationModel application )
3261
{
33-
foreach ( var controller in application.Controllers )
62+
if ( conventionBuilder.Count == 0 )
63+
{
64+
foreach ( var controller in application.Controllers )
65+
{
66+
ApplyAttributeOrImplicitConventions( controller );
67+
}
68+
}
69+
else
70+
{
71+
foreach ( var controller in application.Controllers )
72+
{
73+
if ( !conventionBuilder.ApplyTo( controller ) )
74+
{
75+
ApplyAttributeOrImplicitConventions( controller );
76+
}
77+
}
78+
}
79+
}
80+
81+
private static bool IsDecoratedWithAttributes( ControllerModel controller )
82+
{
83+
Contract.Requires( controller != null );
84+
85+
foreach ( var attribute in controller.Attributes )
86+
{
87+
if ( attribute is IApiVersionProvider || attribute is IApiVersionNeutral )
88+
{
89+
return true;
90+
}
91+
}
92+
93+
return false;
94+
}
95+
96+
private void ApplyImplicitConventions( ControllerModel controller )
97+
{
98+
Contract.Requires( controller != null );
99+
100+
controller.SetProperty( implicitVersionModel );
101+
102+
foreach ( var action in controller.Actions )
103+
{
104+
action.SetProperty( implicitVersionModel );
105+
}
106+
}
107+
108+
private void ApplyAttributeOrImplicitConventions( ControllerModel controller )
109+
{
110+
Contract.Requires( controller != null );
111+
112+
if ( IsDecoratedWithAttributes( controller ) )
113+
{
114+
var conventions = new ControllerApiVersionConventionBuilder<ControllerModel>();
115+
conventions.ApplyTo( controller );
116+
}
117+
else
34118
{
35-
conventionBuilder.ApplyTo( controller );
119+
ApplyImplicitConventions( controller );
36120
}
37121
}
38122
}

0 commit comments

Comments
 (0)