44 using Microsoft . AspNetCore . Mvc . ApplicationModels ;
55 using Microsoft . AspNetCore . Mvc . Controllers ;
66 using Microsoft . AspNetCore . Mvc . ModelBinding ;
7+ using Microsoft . AspNetCore . Mvc . Routing ;
78 using Microsoft . AspNetCore . Mvc . Versioning ;
9+ using Microsoft . AspNetCore . Routing ;
810 using System ;
911 using System . Collections . Generic ;
1012 using System . Diagnostics . Contracts ;
@@ -22,16 +24,27 @@ public class VersionedApiDescriptionProvider : IApiDescriptionProvider
2224 /// Initializes a new instance of <see cref="VersionedApiDescriptionProvider"/> class.
2325 /// </summary>
2426 /// <param name="groupNameFormatter">The <see cref="IApiVersionGroupNameFormatter">formatter</see> used to get group names for API versions.</param>
27+ /// <param name="metadadataProvider">The <see cref="IModelMetadataProvider">provider</see> used to retrieve model metadata.</param>
2528 public VersionedApiDescriptionProvider ( IApiVersionGroupNameFormatter groupNameFormatter , IModelMetadataProvider metadadataProvider )
2629 {
2730 Arg . NotNull ( groupNameFormatter , nameof ( groupNameFormatter ) ) ;
28- GroupNameFormatter = groupNameFormatter ;
31+ Arg . NotNull ( metadadataProvider , nameof ( metadadataProvider ) ) ;
2932
30- this . metadadataProvider = metadadataProvider ;
33+ GroupNameFormatter = groupNameFormatter ;
34+ MetadadataProvider = metadadataProvider ;
3135 }
3236
33- readonly IModelMetadataProvider metadadataProvider ;
37+ /// <summary>
38+ /// Gets the group name formatter associated with the API description provider.
39+ /// </summary>
40+ /// <value>The <see cref="IApiVersionGroupNameFormatter">group name formatter</see> used to format group names.</value>
41+ protected IApiVersionGroupNameFormatter GroupNameFormatter { get ; }
3442
43+ /// <summary>
44+ /// Gets the model metadata provider associated with the API description provider.
45+ /// </summary>
46+ /// <value>The <see cref="IModelMetadataProvider">provider</see> used to retrieve model metadata.</value>
47+ protected IModelMetadataProvider MetadadataProvider { get ; }
3548
3649 /// <summary>
3750 /// Gets the order prescendence of the current API description provider.
@@ -40,13 +53,7 @@ public VersionedApiDescriptionProvider( IApiVersionGroupNameFormatter groupNameF
4053 public virtual int Order => 0 ;
4154
4255 /// <summary>
43- /// Gets the group name formatter associated with the provider.
44- /// </summary>
45- /// <value>The <see cref="IApiVersionGroupNameFormatter">group name formatter</see> used to format group names.</value>
46- protected IApiVersionGroupNameFormatter GroupNameFormatter { get ; }
47-
48- /// <summary>
49- /// Determines whether the specified action should be explored.
56+ /// Determines whether the specified action should be explored for the indicated API version.
5057 /// </summary>
5158 /// <param name="actionDescriptor">The <see cref="ActionDescriptor">action</see> to evaluate.</param>
5259 /// <param name="apiVersion">The <see cref="ApiVersion">API version</see> for action being explored.</param>
@@ -91,6 +98,7 @@ public virtual void OnProvidersExecuted( ApiDescriptionProviderContext context )
9198 }
9299
93100 var groupResults = new List < ApiDescription > ( ) ;
101+ var stringModelMetadata = new Lazy < ModelMetadata > ( ( ) => MetadadataProvider . GetMetadataForType ( typeof ( string ) ) ) ;
94102
95103 foreach ( var version in FlattenApiVersions ( results ) )
96104 {
@@ -102,21 +110,15 @@ public virtual void OnProvidersExecuted( ApiDescriptionProviderContext context )
102110
103111 if ( ShouldExploreAction ( action , version ) )
104112 {
105- // BUG: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/315
106- // BUG: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/355
107- // HACK: this happens when the the ApiVersionRouteConstraint is used. it doesn't produce model metadata; not even string. can it be prevented beyond the Swagger/Swashuckle fix?
108- foreach ( var param in result . ParameterDescriptions )
113+ foreach ( var parameter in result . ParameterDescriptions )
109114 {
110- if ( param . ModelMetadata == null )
111- {
112- param . ModelMetadata = metadadataProvider . GetMetadataForType ( typeof ( string ) ) ;
113- }
115+ ApplyModelMetadataIfNecessary ( parameter , stringModelMetadata ) ;
114116 }
115117
116118 var groupResult = result . Clone ( ) ;
117119
118120 groupResult . GroupName = groupName ;
119- groupResult . SetProperty ( version ) ;
121+ groupResult . SetApiVersion ( version ) ;
120122 groupResults . Add ( groupResult ) ;
121123 }
122124 }
@@ -134,6 +136,7 @@ public virtual void OnProvidersExecuted( ApiDescriptionProviderContext context )
134136 /// Occurs when the providers are being executed.
135137 /// </summary>
136138 /// <param name="context">The current <see cref="ApiDescriptionProviderContext">execution context</see>.</param>
139+ /// <remarks>The default implementation performs no operation.</remarks>
137140 public virtual void OnProvidersExecuting ( ApiDescriptionProviderContext context ) { }
138141
139142 static IEnumerable < ApiVersion > FlattenApiVersions ( IEnumerable < ApiDescription > descriptions )
@@ -157,5 +160,28 @@ static IEnumerable<ApiVersion> FlattenApiVersions( IEnumerable<ApiDescription> d
157160
158161 return versions . OrderBy ( v => v ) ;
159162 }
163+
164+ static void ApplyModelMetadataIfNecessary ( ApiParameterDescription parameter , Lazy < ModelMetadata > stringModelMetadata )
165+ {
166+ if ( parameter . ModelMetadata != null )
167+ {
168+ return ;
169+ }
170+
171+ var constraints = parameter ? . RouteInfo . Constraints ?? Empty < IRouteConstraint > ( ) ;
172+
173+ // versioning by URL path segment is the only method that the built-in api explorer will detect as a parameter.
174+ // since the route parameter likely has no counterpart in model binding, fill in what the model metadata "should"
175+ // be. this is only required when the ApiVersionRouteConstraint is found. all other methods such as versioning
176+ // by query string, header, or media type will require service authors to add the corresponding parameter in
177+ // tools such as Swagger. treat the api version as a string for the purposes of api exploration.
178+ if ( constraints . OfType < ApiVersionRouteConstraint > ( ) . Any ( ) )
179+ {
180+ var modelMetadata = stringModelMetadata . Value ;
181+
182+ parameter . ModelMetadata = modelMetadata ;
183+ parameter . Type = modelMetadata . ModelType ;
184+ }
185+ }
160186 }
161187}
0 commit comments