Skip to content

Commit 39af0ed

Browse files
Resolve route parameter by route constraint type or name. Relates to #812
1 parent 045980e commit 39af0ed

File tree

1 file changed

+75
-11
lines changed

1 file changed

+75
-11
lines changed

src/AspNetCore/WebApi/src/Asp.Versioning.Mvc.ApiExplorer/ApiVersionParameterDescriptionContext.cs

Lines changed: 75 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ namespace Asp.Versioning.ApiExplorer;
77
using Microsoft.AspNetCore.Mvc.ApiExplorer;
88
using Microsoft.AspNetCore.Mvc.ModelBinding;
99
using Microsoft.AspNetCore.Routing;
10+
using Microsoft.AspNetCore.Routing.Patterns;
1011
using static Asp.Versioning.ApiVersionParameterLocation;
1112
using static System.Linq.Enumerable;
1213
using static System.StringComparison;
@@ -36,9 +37,9 @@ public ApiVersionParameterDescriptionContext(
3637
ApiExplorerOptions options )
3738
{
3839
Options = options ?? throw new ArgumentNullException( nameof( options ) );
39-
ApiDescription = apiDescription;
40-
ApiVersion = apiVersion;
41-
ModelMetadata = modelMetadata;
40+
ApiDescription = apiDescription ?? throw new ArgumentNullException( nameof( apiDescription ) );
41+
ApiVersion = apiVersion ?? throw new ArgumentNullException( nameof( apiVersion ) );
42+
ModelMetadata = modelMetadata ?? throw new ArgumentNullException( nameof( modelMetadata ) );
4243
optional = options.AssumeDefaultVersionWhenUnspecified && apiVersion == options.DefaultApiVersion;
4344
}
4445

@@ -158,13 +159,8 @@ protected virtual void AddHeader( string name )
158159
/// </summary>
159160
protected virtual void UpdateUrlSegment()
160161
{
161-
var query = from description in ApiDescription.ParameterDescriptions
162-
let routeInfo = description.RouteInfo
163-
where routeInfo != null
164-
let constraints = routeInfo.Constraints ?? Empty<IRouteConstraint>()
165-
where constraints.OfType<ApiVersionRouteConstraint>().Any()
166-
select description;
167-
var parameter = query.FirstOrDefault();
162+
var parameter = FindByRouteConstraintType( ApiDescription ) ??
163+
FindByRouteConstraintName( ApiDescription, Options.RouteConstraintName );
168164

169165
if ( parameter == null )
170166
{
@@ -184,7 +180,7 @@ where constraints.OfType<ApiVersionRouteConstraint>().Any()
184180

185181
if ( parameter.ParameterDescriptor == null )
186182
{
187-
parameter.ParameterDescriptor = new ParameterDescriptor()
183+
parameter.ParameterDescriptor = new()
188184
{
189185
Name = parameter.Name,
190186
ParameterType = typeof( ApiVersion ),
@@ -245,6 +241,74 @@ protected virtual void AddMediaTypeParameter( string name )
245241
}
246242
}
247243

244+
private static ApiParameterDescription? FindByRouteConstraintType( ApiDescription description )
245+
{
246+
var parameters = description.ParameterDescriptions;
247+
248+
for ( var i = 0; i < parameters.Count; i++ )
249+
{
250+
var parameter = parameters[i];
251+
252+
if ( parameter.RouteInfo is ApiParameterRouteInfo routeInfo &&
253+
routeInfo.Constraints is IEnumerable<IRouteConstraint> constraints &&
254+
constraints.OfType<ApiVersionRouteConstraint>().Any() )
255+
{
256+
return parameter;
257+
}
258+
}
259+
260+
return default;
261+
}
262+
263+
private static ApiParameterDescription? FindByRouteConstraintName( ApiDescription description, string constraintName )
264+
{
265+
var relativePath = description.RelativePath;
266+
267+
if ( string.IsNullOrEmpty( relativePath ) )
268+
{
269+
return default;
270+
}
271+
272+
var routePattern = RoutePatternFactory.Parse( relativePath );
273+
var parameters = routePattern.Parameters;
274+
var parameterDescriptions = description.ParameterDescriptions;
275+
276+
for ( var i = 0; i < parameters.Count; i++ )
277+
{
278+
var parameter = parameters[i];
279+
var policies = parameter.ParameterPolicies;
280+
281+
for ( var j = 0; j < policies.Count; j++ )
282+
{
283+
if ( !constraintName.Equals( policies[j].Content, Ordinal ) )
284+
{
285+
continue;
286+
}
287+
288+
for ( var k = 0; k < parameterDescriptions.Count; k++ )
289+
{
290+
var parameterDescription = parameterDescriptions[k];
291+
292+
if ( parameterDescription.Name != parameter.Name &&
293+
parameterDescription.ParameterDescriptor?.ParameterType != typeof( ApiVersion ) )
294+
{
295+
continue;
296+
}
297+
298+
var token = $"{parameter.Name}:{constraintName}";
299+
300+
parameterDescription.Name = parameter.Name;
301+
description.RelativePath = relativePath.Replace( token, parameter.Name, Ordinal );
302+
parameterDescription.Source = BindingSource.Path;
303+
304+
return parameterDescription;
305+
}
306+
}
307+
}
308+
309+
return default;
310+
}
311+
248312
private ApiParameterDescription NewApiVersionParameter( string name, BindingSource source )
249313
{
250314
var parameter = new ApiParameterDescription()

0 commit comments

Comments
 (0)