@@ -7,6 +7,7 @@ namespace Asp.Versioning.ApiExplorer;
77using Microsoft . AspNetCore . Mvc . ApiExplorer ;
88using Microsoft . AspNetCore . Mvc . ModelBinding ;
99using Microsoft . AspNetCore . Routing ;
10+ using Microsoft . AspNetCore . Routing . Patterns ;
1011using static Asp . Versioning . ApiVersionParameterLocation ;
1112using static System . Linq . Enumerable ;
1213using 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