3
3
4
4
using System ;
5
5
using System . Collections . Generic ;
6
+ using System . Diagnostics . Contracts ;
6
7
using System . Linq ;
7
8
using System . Threading . Tasks ;
8
9
using Microsoft . AspNet . Mvc . HeaderValueAbstractions ;
9
10
using Microsoft . AspNet . Mvc . ModelBinding ;
11
+ using Microsoft . AspNet . Routing ;
12
+ using Microsoft . AspNet . Routing . Template ;
10
13
using Microsoft . Framework . DependencyInjection ;
11
14
12
15
namespace Microsoft . AspNet . Mvc . Description
@@ -19,6 +22,7 @@ public class DefaultApiDescriptionProvider : INestedProvider<ApiDescriptionProvi
19
22
{
20
23
private readonly IOutputFormattersProvider _formattersProvider ;
21
24
private readonly IModelMetadataProvider _modelMetadataProvider ;
25
+ private readonly IInlineConstraintResolver _constraintResolver ;
22
26
23
27
/// <summary>
24
28
/// Creates a new instance of <see cref="DefaultApiDescriptionProvider"/>.
@@ -27,10 +31,12 @@ public class DefaultApiDescriptionProvider : INestedProvider<ApiDescriptionProvi
27
31
/// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
28
32
public DefaultApiDescriptionProvider (
29
33
IOutputFormattersProvider formattersProvider ,
34
+ IInlineConstraintResolver constraintResolver ,
30
35
IModelMetadataProvider modelMetadataProvider )
31
36
{
32
37
_formattersProvider = formattersProvider ;
33
38
_modelMetadataProvider = modelMetadataProvider ;
39
+ _constraintResolver = constraintResolver ;
34
40
}
35
41
36
42
/// <inheritdoc />
@@ -60,25 +66,23 @@ public void Invoke(ApiDescriptionProviderContext context, Action callNext)
60
66
}
61
67
62
68
private ApiDescription CreateApiDescription (
63
- ControllerActionDescriptor action ,
64
- string httpMethod ,
69
+ ControllerActionDescriptor action ,
70
+ string httpMethod ,
65
71
string groupName )
66
72
{
73
+ var parsedTemplate = ParseTemplate ( action ) ;
74
+
67
75
var apiDescription = new ApiDescription ( )
68
76
{
69
77
ActionDescriptor = action ,
70
78
GroupName = groupName ,
71
79
HttpMethod = httpMethod ,
72
- RelativePath = GetRelativePath ( action ) ,
80
+ RelativePath = GetRelativePath ( parsedTemplate ) ,
73
81
} ;
74
82
75
- if ( action . Parameters != null )
76
- {
77
- foreach ( var parameter in action . Parameters )
78
- {
79
- apiDescription . ParameterDescriptions . Add ( GetParameter ( parameter ) ) ;
80
- }
81
- }
83
+ var templateParameters = parsedTemplate ? . Parameters ? . ToList ( ) ?? new List < TemplatePart > ( ) ;
84
+
85
+ GetParameters ( apiDescription , action . Parameters , templateParameters ) ;
82
86
83
87
var responseMetadataAttributes = GetResponseMetadataAttributes ( action ) ;
84
88
@@ -103,13 +107,13 @@ private ApiDescription CreateApiDescription(
103
107
apiDescription . ResponseType = runtimeReturnType ;
104
108
105
109
apiDescription . ResponseModelMetadata = _modelMetadataProvider . GetMetadataForType (
106
- modelAccessor : null ,
110
+ modelAccessor : null ,
107
111
modelType : runtimeReturnType ) ;
108
112
109
113
var formats = GetResponseFormats (
110
- action ,
111
- responseMetadataAttributes ,
112
- declaredReturnType ,
114
+ action ,
115
+ responseMetadataAttributes ,
116
+ declaredReturnType ,
113
117
runtimeReturnType ) ;
114
118
115
119
foreach ( var format in formats )
@@ -121,6 +125,44 @@ private ApiDescription CreateApiDescription(
121
125
return apiDescription ;
122
126
}
123
127
128
+ private void GetParameters (
129
+ ApiDescription apiDescription ,
130
+ IList < ParameterDescriptor > parameterDescriptors ,
131
+ IList < TemplatePart > templateParameters )
132
+ {
133
+ if ( parameterDescriptors != null )
134
+ {
135
+ foreach ( var parameter in parameterDescriptors )
136
+ {
137
+ // Process together parameters that appear on the path template and on the
138
+ // action descriptor and do not come from the body.
139
+ TemplatePart templateParameter = null ;
140
+ if ( parameter . BodyParameterInfo == null )
141
+ {
142
+ templateParameter = templateParameters
143
+ . FirstOrDefault ( p => p . Name . Equals ( parameter . Name , StringComparison . OrdinalIgnoreCase ) ) ;
144
+
145
+ if ( templateParameter != null )
146
+ {
147
+ templateParameters . Remove ( templateParameter ) ;
148
+ }
149
+ }
150
+
151
+ apiDescription . ParameterDescriptions . Add ( GetParameter ( parameter , templateParameter ) ) ;
152
+ }
153
+ }
154
+
155
+ if ( templateParameters . Count > 0 )
156
+ {
157
+ // Process parameters that only appear on the path template if any.
158
+ foreach ( var templateParameter in templateParameters )
159
+ {
160
+ var parameterDescription = GetParameter ( parameterDescriptor : null , templateParameter : templateParameter ) ;
161
+ apiDescription . ParameterDescriptions . Add ( parameterDescription ) ;
162
+ }
163
+ }
164
+ }
165
+
124
166
private IEnumerable < string > GetHttpMethods ( ControllerActionDescriptor action )
125
167
{
126
168
if ( action . ActionConstraints != null && action . ActionConstraints . Count > 0 )
@@ -133,23 +175,93 @@ private IEnumerable<string> GetHttpMethods(ControllerActionDescriptor action)
133
175
}
134
176
}
135
177
136
- private string GetRelativePath ( ControllerActionDescriptor action )
178
+ private RouteTemplate ParseTemplate ( ControllerActionDescriptor action )
137
179
{
138
- // This is a placeholder for functionality which will correctly generate the relative path
139
- // stub of an action. See: #885
140
180
if ( action . AttributeRouteInfo != null &&
141
181
action . AttributeRouteInfo . Template != null )
142
182
{
143
- return action . AttributeRouteInfo . Template ;
183
+ return TemplateParser . Parse ( action . AttributeRouteInfo . Template , _constraintResolver ) ;
144
184
}
145
185
146
186
return null ;
147
187
}
148
188
149
- private ApiParameterDescription GetParameter ( ParameterDescriptor parameter )
189
+ private string GetRelativePath ( RouteTemplate parsedTemplate )
190
+ {
191
+ if ( parsedTemplate == null )
192
+ {
193
+ return null ;
194
+ }
195
+
196
+ var segments = new List < string > ( ) ;
197
+
198
+ foreach ( var segment in parsedTemplate . Segments )
199
+ {
200
+ var currentSegment = "" ;
201
+ foreach ( var part in segment . Parts )
202
+ {
203
+ if ( part . IsLiteral )
204
+ {
205
+ currentSegment += part . Text ;
206
+ }
207
+ else if ( part . IsParameter )
208
+ {
209
+ currentSegment += "{" + part . Name + "}" ;
210
+ }
211
+ }
212
+
213
+ segments . Add ( currentSegment ) ;
214
+ }
215
+
216
+ return string . Join ( "/" , segments ) ;
217
+ }
218
+
219
+ private ApiParameterDescription GetParameter (
220
+ ParameterDescriptor parameterDescriptor ,
221
+ TemplatePart templateParameter )
150
222
{
151
223
// This is a placeholder based on currently available functionality for parameters. See #886.
152
- var resourceParameter = new ApiParameterDescription ( )
224
+ ApiParameterDescription parameterDescription = null ;
225
+
226
+ if ( templateParameter != null && parameterDescriptor == null )
227
+ {
228
+ // The parameter is part of the route template but not part of the ActionDescriptor.
229
+
230
+ // For now if a parameter is part of the template we will asume its value comes from the path.
231
+ // We will be more accurate when we implement #886.
232
+ parameterDescription = CreateParameterFromTemplate ( templateParameter ) ;
233
+ }
234
+ else if ( templateParameter != null && parameterDescriptor != null )
235
+ {
236
+ // The parameter is part of the route template and part of the ActionDescriptor.
237
+ parameterDescription = CreateParameterFromTemplateAndParameterDescriptor (
238
+ templateParameter ,
239
+ parameterDescriptor ) ;
240
+ }
241
+ else if ( templateParameter == null && parameterDescriptor != null )
242
+ {
243
+ // The parameter is part of the ActionDescriptor but is not part of the route template.
244
+ parameterDescription = CreateParameterFromParameterDescriptor ( parameterDescriptor ) ;
245
+ }
246
+ else
247
+ {
248
+ // We will never call this method with templateParameter == null && parameterDescriptor == null
249
+ Contract . Assert ( parameterDescriptor != null ) ;
250
+ }
251
+
252
+ if ( parameterDescription . Type != null )
253
+ {
254
+ parameterDescription . ModelMetadata = _modelMetadataProvider . GetMetadataForType (
255
+ modelAccessor : null ,
256
+ modelType : parameterDescription . Type ) ;
257
+ }
258
+
259
+ return parameterDescription ;
260
+ }
261
+
262
+ private static ApiParameterDescription CreateParameterFromParameterDescriptor ( ParameterDescriptor parameter )
263
+ {
264
+ var resourceParameter = new ApiParameterDescription
153
265
{
154
266
IsOptional = parameter . IsOptional ,
155
267
Name = parameter . Name ,
@@ -158,8 +270,8 @@ private ApiParameterDescription GetParameter(ParameterDescriptor parameter)
158
270
159
271
if ( parameter . ParameterBindingInfo != null )
160
272
{
161
- resourceParameter . Type = parameter . ParameterBindingInfo . ParameterType ;
162
273
resourceParameter . Source = ApiParameterSource . Query ;
274
+ resourceParameter . Type = parameter . ParameterBindingInfo . ParameterType ;
163
275
}
164
276
165
277
if ( parameter . BodyParameterInfo != null )
@@ -168,16 +280,49 @@ private ApiParameterDescription GetParameter(ParameterDescriptor parameter)
168
280
resourceParameter . Source = ApiParameterSource . Body ;
169
281
}
170
282
171
- if ( resourceParameter . Type != null )
283
+ return resourceParameter ;
284
+ }
285
+
286
+ private static ApiParameterDescription CreateParameterFromTemplateAndParameterDescriptor (
287
+ TemplatePart templateParameter ,
288
+ ParameterDescriptor parameter )
289
+ {
290
+ var resourceParameter = new ApiParameterDescription
291
+ {
292
+ Source = ApiParameterSource . Path ,
293
+ IsOptional = parameter . IsOptional && IsOptionalParameter ( templateParameter ) ,
294
+ Name = parameter . Name ,
295
+ ParameterDescriptor = parameter ,
296
+ Constraint = templateParameter . InlineConstraint ,
297
+ DefaultValue = templateParameter . DefaultValue ,
298
+ } ;
299
+
300
+ if ( parameter . ParameterBindingInfo != null )
172
301
{
173
- resourceParameter . ModelMetadata = _modelMetadataProvider . GetMetadataForType (
174
- modelAccessor : null ,
175
- modelType : resourceParameter . Type ) ;
302
+ resourceParameter . Type = parameter . ParameterBindingInfo . ParameterType ;
176
303
}
177
304
178
305
return resourceParameter ;
179
306
}
180
307
308
+ private static bool IsOptionalParameter ( TemplatePart templateParameter )
309
+ {
310
+ return templateParameter . IsOptional || templateParameter . DefaultValue != null ;
311
+ }
312
+
313
+ private static ApiParameterDescription CreateParameterFromTemplate ( TemplatePart templateParameter )
314
+ {
315
+ return new ApiParameterDescription
316
+ {
317
+ Source = ApiParameterSource . Path ,
318
+ IsOptional = IsOptionalParameter ( templateParameter ) ,
319
+ Name = templateParameter . Name ,
320
+ ParameterDescriptor = null ,
321
+ Constraint = templateParameter . InlineConstraint ,
322
+ DefaultValue = templateParameter . DefaultValue ,
323
+ } ;
324
+ }
325
+
181
326
private IReadOnlyList < ApiResponseFormat > GetResponseFormats (
182
327
ControllerActionDescriptor action ,
183
328
IApiResponseMetadataProvider [ ] responseMetadataAttributes ,
@@ -220,7 +365,7 @@ private IReadOnlyList<ApiResponseFormat> GetResponseFormats(
220
365
}
221
366
}
222
367
}
223
- }
368
+ }
224
369
225
370
return results ;
226
371
}
0 commit comments