Skip to content

Commit de9bc84

Browse files
Chris Martinezcommonsensesoftware
Chris Martinez
authored andcommitted
Validate candidates before matching. Fixes #600
1 parent 678e1e3 commit de9bc84

File tree

3 files changed

+47
-30
lines changed

3 files changed

+47
-30
lines changed

src/Microsoft.AspNetCore.Mvc.Versioning/Routing/ApiVersionMatcherPolicy.cs

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -95,33 +95,28 @@ public Task ApplyAsync( HttpContext httpContext, CandidateSet candidates )
9595
httpContext.Features.Get<IApiVersioningFeature>().RequestedApiVersion = apiVersion;
9696
}
9797

98-
var finalMatches = EvaluateApiVersion( candidates, apiVersion );
99-
100-
if ( finalMatches.Count == 0 )
98+
if ( !MatchesApiVersion( candidates, apiVersion ) )
10199
{
102100
httpContext.SetEndpoint( ClientError( httpContext, candidates ) );
103101
}
104-
else
105-
{
106-
for ( var i = 0; i < finalMatches.Count; i++ )
107-
{
108-
var (index, _, valid) = finalMatches[i];
109-
candidates.SetValidity( index, valid );
110-
}
111-
}
112102

113103
return CompletedTask;
114104
}
115105

116-
static IReadOnlyList<(int Index, ActionDescriptor Action, bool Valid)> EvaluateApiVersion( CandidateSet candidates, ApiVersion? apiVersion )
106+
static bool MatchesApiVersion( CandidateSet candidates, ApiVersion? apiVersion )
117107
{
118-
var bestMatches = new List<(int Index, ActionDescriptor Action, bool)>();
119-
var implicitMatches = new List<(int, ActionDescriptor, bool)>();
108+
var bestMatches = new List<int>();
109+
var implicitMatches = new List<int>();
120110

121111
for ( var i = 0; i < candidates.Count; i++ )
122112
{
113+
if ( !candidates.IsValidCandidate( i ) )
114+
{
115+
continue;
116+
}
117+
123118
ref var candidate = ref candidates[i];
124-
var action = candidate.Endpoint.Metadata?.GetMetadata<ActionDescriptor>();
119+
var action = candidate.Endpoint.Metadata.GetMetadata<ActionDescriptor>();
125120

126121
if ( action == null )
127122
{
@@ -134,10 +129,10 @@ public Task ApplyAsync( HttpContext httpContext, CandidateSet candidates )
134129
switch ( action.MappingTo( apiVersion ) )
135130
{
136131
case Explicit:
137-
bestMatches.Add( (i, action, candidates.IsValidCandidate( i )) );
132+
bestMatches.Add( i );
138133
break;
139134
case Implicit:
140-
implicitMatches.Add( (i, action, candidates.IsValidCandidate( i )) );
135+
implicitMatches.Add( i );
141136
break;
142137
}
143138

@@ -149,20 +144,39 @@ public Task ApplyAsync( HttpContext httpContext, CandidateSet candidates )
149144
switch ( bestMatches.Count )
150145
{
151146
case 0:
152-
bestMatches.AddRange( implicitMatches );
153-
break;
147+
if ( implicitMatches.Count == 0 )
148+
{
149+
return false;
150+
}
151+
152+
for ( var i = 0; i < implicitMatches.Count; i++ )
153+
{
154+
candidates.SetValidity( implicitMatches[i], true );
155+
}
156+
157+
return true;
154158
case 1:
155-
var model = bestMatches[0].Action.GetApiVersionModel();
159+
ref var candidate = ref candidates[bestMatches[0]];
160+
var action = candidate.Endpoint.Metadata.GetMetadata<ActionDescriptor>();
161+
var model = action.GetApiVersionModel();
156162

157163
if ( model.IsApiVersionNeutral )
158164
{
159-
bestMatches.AddRange( implicitMatches );
165+
for ( var i = 0; i < implicitMatches.Count; i++ )
166+
{
167+
candidates.SetValidity( implicitMatches[i], true );
168+
}
160169
}
161170

162171
break;
163172
}
164173

165-
return bestMatches.ToArray();
174+
for ( var i = 0; i < bestMatches.Count; i++ )
175+
{
176+
candidates.SetValidity( bestMatches[i], true );
177+
}
178+
179+
return true;
166180
}
167181

168182
bool IsRequestedApiVersionAmbiguous( HttpContext httpContext, out ApiVersion? apiVersion )
@@ -195,8 +209,13 @@ ApiVersion TrySelectApiVersion( HttpContext httpContext, CandidateSet candidates
195209

196210
for ( var i = 0; i < candidates.Count; i++ )
197211
{
212+
if ( !candidates.IsValidCandidate( i ) )
213+
{
214+
continue;
215+
}
216+
198217
ref var candidate = ref candidates[i];
199-
var model = candidate.Endpoint.Metadata?.GetMetadata<ActionDescriptor>()?.GetApiVersionModel();
218+
var model = candidate.Endpoint.Metadata.GetMetadata<ActionDescriptor>()?.GetApiVersionModel();
200219

201220
if ( model != null )
202221
{

test/Microsoft.AspNetCore.Mvc.Acceptance.Tests/Mvc/Basic/given a versioned Controller/when using a query string and split into two types.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,16 @@ public async Task then_get_with_integer_id_should_return_200()
6161
}
6262

6363
[Fact]
64-
public async Task then_get_returns_400_or_405_with_invalid_id()
64+
public async Task then_get_returns_400_with_invalid_id()
6565
{
6666
// arrange
6767
var requestUrl = "api/values/abc?api-version=2.0";
68-
var statusCode = UsingEndpointRouting ? NotFound : BadRequest;
6968

7069
// act
7170
var response = await GetAsync( requestUrl );
7271

7372
// assert
74-
response.StatusCode.Should().Be( statusCode );
73+
response.StatusCode.Should().Be( BadRequest );
7574
}
7675

7776
[Theory]

test/Microsoft.AspNetCore.Mvc.Acceptance.Tests/Mvc/Basic/given a versioned Controller/when using a url segment.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,16 @@ public async Task then_post_should_return_201( string version )
6565
}
6666

6767
[Fact]
68-
public async Task then_get_returns_400_or_405_with_invalid_id()
68+
public async Task then_get_returns_400_with_invalid_id()
6969
{
7070
// arrange
7171
var requestUrl = "api/v2/helloworld/abc";
72-
var statusCode = UsingEndpointRouting ? NotFound : BadRequest;
7372

7473
// act
7574
var response = await GetAsync( requestUrl );
7675

77-
// assert
78-
response.StatusCode.Should().Be( statusCode );
76+
// asserts
77+
response.StatusCode.Should().Be( BadRequest );
7978
}
8079

8180
[Theory]

0 commit comments

Comments
 (0)