4
4
using Microsoft . AspNetCore . Mvc . ApplicationModels ;
5
5
using Microsoft . AspNetCore . Mvc . Controllers ;
6
6
using Microsoft . AspNetCore . Mvc . ModelBinding ;
7
+ using Microsoft . AspNetCore . Mvc . Routing ;
7
8
using Microsoft . AspNetCore . Mvc . Versioning ;
9
+ using Microsoft . AspNetCore . Routing ;
8
10
using System ;
9
11
using System . Collections . Generic ;
10
12
using System . Diagnostics . Contracts ;
@@ -22,16 +24,27 @@ public class VersionedApiDescriptionProvider : IApiDescriptionProvider
22
24
/// Initializes a new instance of <see cref="VersionedApiDescriptionProvider"/> class.
23
25
/// </summary>
24
26
/// <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>
25
28
public VersionedApiDescriptionProvider ( IApiVersionGroupNameFormatter groupNameFormatter , IModelMetadataProvider metadadataProvider )
26
29
{
27
30
Arg . NotNull ( groupNameFormatter , nameof ( groupNameFormatter ) ) ;
28
- GroupNameFormatter = groupNameFormatter ;
31
+ Arg . NotNull ( metadadataProvider , nameof ( metadadataProvider ) ) ;
29
32
30
- this . metadadataProvider = metadadataProvider ;
33
+ GroupNameFormatter = groupNameFormatter ;
34
+ MetadadataProvider = metadadataProvider ;
31
35
}
32
36
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 ; }
34
42
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 ; }
35
48
36
49
/// <summary>
37
50
/// Gets the order prescendence of the current API description provider.
@@ -40,13 +53,7 @@ public VersionedApiDescriptionProvider( IApiVersionGroupNameFormatter groupNameF
40
53
public virtual int Order => 0 ;
41
54
42
55
/// <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.
50
57
/// </summary>
51
58
/// <param name="actionDescriptor">The <see cref="ActionDescriptor">action</see> to evaluate.</param>
52
59
/// <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 )
91
98
}
92
99
93
100
var groupResults = new List < ApiDescription > ( ) ;
101
+ var stringModelMetadata = new Lazy < ModelMetadata > ( ( ) => MetadadataProvider . GetMetadataForType ( typeof ( string ) ) ) ;
94
102
95
103
foreach ( var version in FlattenApiVersions ( results ) )
96
104
{
@@ -102,21 +110,15 @@ public virtual void OnProvidersExecuted( ApiDescriptionProviderContext context )
102
110
103
111
if ( ShouldExploreAction ( action , version ) )
104
112
{
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 )
109
114
{
110
- if ( param . ModelMetadata == null )
111
- {
112
- param . ModelMetadata = metadadataProvider . GetMetadataForType ( typeof ( string ) ) ;
113
- }
115
+ ApplyModelMetadataIfNecessary ( parameter , stringModelMetadata ) ;
114
116
}
115
117
116
118
var groupResult = result . Clone ( ) ;
117
119
118
120
groupResult . GroupName = groupName ;
119
- groupResult . SetProperty ( version ) ;
121
+ groupResult . SetApiVersion ( version ) ;
120
122
groupResults . Add ( groupResult ) ;
121
123
}
122
124
}
@@ -134,6 +136,7 @@ public virtual void OnProvidersExecuted( ApiDescriptionProviderContext context )
134
136
/// Occurs when the providers are being executed.
135
137
/// </summary>
136
138
/// <param name="context">The current <see cref="ApiDescriptionProviderContext">execution context</see>.</param>
139
+ /// <remarks>The default implementation performs no operation.</remarks>
137
140
public virtual void OnProvidersExecuting ( ApiDescriptionProviderContext context ) { }
138
141
139
142
static IEnumerable < ApiVersion > FlattenApiVersions ( IEnumerable < ApiDescription > descriptions )
@@ -157,5 +160,28 @@ static IEnumerable<ApiVersion> FlattenApiVersions( IEnumerable<ApiDescription> d
157
160
158
161
return versions . OrderBy ( v => v ) ;
159
162
}
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
+ }
160
186
}
161
187
}
0 commit comments