Skip to content

Commit e523c61

Browse files
Add more validation for WithApiVersionSet on an endpoint
1 parent 8fe7acb commit e523c61

File tree

5 files changed

+87
-10
lines changed

5 files changed

+87
-10
lines changed

src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/EndpointBuilderFinalizer.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,18 +120,16 @@ private static bool ReportApiVersions( IList<object> metadata )
120120

121121
private static ApiVersionSet? GetApiVersionSet( IList<object> metadata )
122122
{
123-
var result = default( ApiVersionSet );
124-
125123
for ( var i = metadata.Count - 1; i >= 0; i-- )
126124
{
127125
if ( metadata[i] is ApiVersionSet set )
128126
{
129-
result ??= set;
130127
metadata.RemoveAt( i );
128+
return set;
131129
}
132130
}
133131

134-
return result;
132+
return default;
135133
}
136134

137135
private static bool TryGetApiVersions( IList<object> metadata, out ApiVersionBuckets buckets )

src/AspNetCore/WebApi/src/Asp.Versioning.Http/Builder/IEndpointConventionBuilderExtensions.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public static TBuilder WithApiVersionSet<TBuilder>(
3232
throw new ArgumentNullException( nameof( apiVersionSet ) );
3333
}
3434

35-
builder.Add( endpoint => endpoint.Metadata.Add( apiVersionSet ) );
35+
builder.Add( endpoint => AddWithValidation( endpoint, apiVersionSet ) );
3636
builder.Finally( EndpointBuilderFinalizer.FinalizeEndpoints );
3737

3838
return builder;
@@ -393,6 +393,27 @@ public static TBuilder ReportApiVersions<TBuilder>( this TBuilder builder )
393393
return builder;
394394
}
395395

396+
private static void AddWithValidation( EndpointBuilder builder, ApiVersionSet versionSet )
397+
{
398+
var metadata = builder.Metadata;
399+
var grouped = builder.ApplicationServices.GetService( typeof( ApiVersionSetBuilder ) ) is not null;
400+
401+
if ( grouped )
402+
{
403+
throw new InvalidOperationException( SR.MultipleVersionSets );
404+
}
405+
406+
for ( var i = 0; i < metadata.Count; i++ )
407+
{
408+
if ( metadata[i] is ApiVersionSet )
409+
{
410+
throw new InvalidOperationException( SR.MultipleVersionSets );
411+
}
412+
}
413+
414+
metadata.Add( versionSet );
415+
}
416+
396417
private static void AdvertiseInApiVersionSet( IList<object> metadata, ApiVersion apiVersion )
397418
{
398419
for ( var i = metadata.Count - 1; i >= 0; i-- )

src/AspNetCore/WebApi/src/Asp.Versioning.Http/SR.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/AspNetCore/WebApi/src/Asp.Versioning.Http/SR.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@
129129
<data name="ConventionAddedAfterEndpointBuilt" xml:space="preserve">
130130
<value>Conventions cannot be added after building the endpoint.</value>
131131
</data>
132+
<data name="MultipleVersionSets" xml:space="preserve">
133+
<value>An endpoint cannot apply multiple API version sets.</value>
134+
</data>
132135
<data name="NoVersionSet" xml:space="preserve">
133136
<value>The endpoint '{0}' does not have an associated API version set. Are you missing a call to {1} or {2}.</value>
134137
</data>

src/AspNetCore/WebApi/test/Asp.Versioning.Http.Tests/Builder/IEndpointConventionBuilderExtensionsTest.cs

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ namespace Asp.Versioning.Builder;
66
using Microsoft.AspNetCore.Builder;
77
using Microsoft.AspNetCore.Http;
88
using Microsoft.AspNetCore.Routing;
9+
using Microsoft.Extensions.DependencyInjection;
910
using Microsoft.Extensions.Options;
1011
using static Asp.Versioning.ApiVersionProviderOptions;
1112

@@ -251,6 +252,56 @@ public void with_api_version_set_should_collate_across_grouped_endpoints()
251252
.BeEquivalentTo( ApiVersionMetadata.Neutral );
252253
}
253254

255+
[Fact]
256+
public void with_api_version_set_should_not_be_allowed_multiple_times()
257+
{
258+
// arrange
259+
var builder = WebApplication.CreateBuilder();
260+
var services = builder.Services;
261+
262+
services.AddControllers();
263+
services.AddApiVersioning();
264+
265+
var app = builder.Build();
266+
var versionSet = new ApiVersionSetBuilder( default ).Build();
267+
var get = app.MapGet( "/", () => Results.Ok() );
268+
IEndpointRouteBuilder endpoints = app;
269+
270+
get.WithApiVersionSet( versionSet );
271+
get.WithApiVersionSet( versionSet );
272+
273+
// act
274+
var build = () => endpoints.DataSources.Single().Endpoints;
275+
276+
// assert
277+
build.Should().Throw<InvalidOperationException>();
278+
}
279+
280+
[Fact]
281+
public void with_api_version_set_should_not_override_existing_metadata()
282+
{
283+
// arrange
284+
var builder = WebApplication.CreateBuilder();
285+
var services = builder.Services;
286+
287+
services.AddControllers();
288+
services.AddApiVersioning();
289+
290+
var app = builder.Build();
291+
var versionSet = new ApiVersionSetBuilder( default ).Build();
292+
var group = app.MapApiGroup();
293+
var get = group.MapGet( "/", () => Results.Ok() );
294+
IEndpointRouteBuilder endpoints = app;
295+
296+
get.WithApiVersionSet( versionSet );
297+
298+
// act
299+
var build = () => endpoints.DataSources.Single().Endpoints;
300+
301+
// assert
302+
build.Should().Throw<InvalidOperationException>();
303+
}
304+
254305
[Fact]
255306
public void report_api_versions_should_add_convention()
256307
{
@@ -696,11 +747,6 @@ public object GetService( Type serviceType )
696747
return options.Value.ApiVersionReader;
697748
}
698749

699-
if ( typeof( IApiVersionSetBuilderFactory ) == serviceType )
700-
{
701-
return new DefaultApiVersionSetBuilderFactory();
702-
}
703-
704750
return null;
705751
}
706752
}

0 commit comments

Comments
 (0)