@@ -28,6 +28,9 @@ internal sealed class OpenApiDocumentService(
28
28
IServiceProvider serviceProvider )
29
29
{
30
30
private readonly OpenApiOptions _options = optionsMonitor . Get ( documentName ) ;
31
+ private readonly OpenApiComponentService _componentService = serviceProvider . GetRequiredKeyedService < OpenApiComponentService > ( documentName ) ;
32
+
33
+ private static readonly OpenApiEncoding _defaultFormEncoding = new OpenApiEncoding { Style = ParameterStyle . Form , Explode = true } ;
31
34
32
35
/// <summary>
33
36
/// Cache of <see cref="OpenApiOperationTransformerContext"/> instances keyed by the
@@ -124,7 +127,7 @@ private Dictionary<OperationType, OpenApiOperation> GetOperations(IGrouping<stri
124
127
return operations ;
125
128
}
126
129
127
- private static OpenApiOperation GetOperation ( ApiDescription description , HashSet < OpenApiTag > capturedTags )
130
+ private OpenApiOperation GetOperation ( ApiDescription description , HashSet < OpenApiTag > capturedTags )
128
131
{
129
132
var tags = GetTags ( description ) ;
130
133
if ( tags != null )
@@ -140,6 +143,7 @@ private static OpenApiOperation GetOperation(ApiDescription description, HashSet
140
143
Description = GetDescription ( description ) ,
141
144
Responses = GetResponses ( description ) ,
142
145
Parameters = GetParameters ( description ) ,
146
+ RequestBody = GetRequestBody ( description ) ,
143
147
Tags = tags ,
144
148
} ;
145
149
return operation ;
@@ -256,4 +260,78 @@ private static OpenApiResponse GetResponse(ApiDescription apiDescription, int st
256
260
}
257
261
return parameters ;
258
262
}
263
+
264
+ private OpenApiRequestBody ? GetRequestBody ( ApiDescription description )
265
+ {
266
+ // Only one parameter can be bound from the body in each request.
267
+ if ( description . TryGetBodyParameter ( out var bodyParameter ) )
268
+ {
269
+ return GetJsonRequestBody ( description . SupportedRequestFormats , bodyParameter ) ;
270
+ }
271
+ // If there are no body parameters, check for form parameters.
272
+ // Note: Form parameters and body parameters cannot exist simultaneously
273
+ // in the same endpoint.
274
+ if ( description . TryGetFormParameters ( out var formParameters ) )
275
+ {
276
+ return GetFormRequestBody ( description . SupportedRequestFormats , formParameters ) ;
277
+ }
278
+ return null ;
279
+ }
280
+
281
+ private OpenApiRequestBody GetFormRequestBody ( IList < ApiRequestFormat > supportedRequestFormats , IEnumerable < ApiParameterDescription > formParameters )
282
+ {
283
+ if ( supportedRequestFormats . Count == 0 )
284
+ {
285
+ // Assume "application/x-www-form-urlencoded" as the default media type
286
+ // to match the default assumed in IFormFeature.
287
+ supportedRequestFormats = [ new ApiRequestFormat { MediaType = "application/x-www-form-urlencoded" } ] ;
288
+ }
289
+
290
+ var requestBody = new OpenApiRequestBody
291
+ {
292
+ Required = formParameters . Any ( parameter => parameter . IsRequired ) ,
293
+ Content = new Dictionary < string , OpenApiMediaType > ( )
294
+ } ;
295
+
296
+ // Forms are represented as objects with properties for each form field.
297
+ var schema = new OpenApiSchema { Type = "object" , Properties = new Dictionary < string , OpenApiSchema > ( ) } ;
298
+ foreach ( var parameter in formParameters )
299
+ {
300
+ schema . Properties [ parameter . Name ] = _componentService . GetOrCreateSchema ( parameter . Type ) ;
301
+ }
302
+
303
+ foreach ( var requestFormat in supportedRequestFormats )
304
+ {
305
+ var contentType = requestFormat . MediaType ;
306
+ requestBody . Content [ contentType ] = new OpenApiMediaType
307
+ {
308
+ Schema = schema ,
309
+ Encoding = new Dictionary < string , OpenApiEncoding > ( ) { [ contentType ] = _defaultFormEncoding }
310
+ } ;
311
+ }
312
+
313
+ return requestBody ;
314
+ }
315
+
316
+ private static OpenApiRequestBody GetJsonRequestBody ( IList < ApiRequestFormat > supportedRequestFormats , ApiParameterDescription bodyParameter )
317
+ {
318
+ if ( supportedRequestFormats . Count == 0 )
319
+ {
320
+ supportedRequestFormats = [ new ApiRequestFormat { MediaType = "application/json" } ] ;
321
+ }
322
+
323
+ var requestBody = new OpenApiRequestBody
324
+ {
325
+ Required = bodyParameter . IsRequired ,
326
+ Content = new Dictionary < string , OpenApiMediaType > ( )
327
+ } ;
328
+
329
+ foreach ( var requestForm in supportedRequestFormats )
330
+ {
331
+ var contentType = requestForm . MediaType ;
332
+ requestBody . Content [ contentType ] = new OpenApiMediaType ( ) ;
333
+ }
334
+
335
+ return requestBody ;
336
+ }
259
337
}
0 commit comments