Skip to content

Commit 4d505d6

Browse files
committed
@io.swagger.v3.oas.annotations.parameters.RequestBody does not work well with @RequestPart. Fixes #3071
1 parent 3190ae2 commit 4d505d6

File tree

3 files changed

+96
-60
lines changed

3 files changed

+96
-60
lines changed

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/RequestBodyService.java

Lines changed: 24 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -257,11 +257,7 @@ public void calculateRequestBodyInfo(Components components, MethodAttributes met
257257
ParameterInfo parameterInfo, RequestBodyInfo requestBodyInfo) {
258258
RequestBody requestBody = requestBodyInfo.getRequestBody();
259259
MethodParameter methodParameter = parameterInfo.getMethodParameter();
260-
// Get it from parameter level, if not present
261-
if (requestBody == null) {
262-
io.swagger.v3.oas.annotations.parameters.RequestBody requestBodyDoc = methodParameter.getParameterAnnotation(io.swagger.v3.oas.annotations.parameters.RequestBody.class);
263-
requestBody = this.buildRequestBodyFromDoc(requestBodyDoc, methodAttributes, components).orElse(null);
264-
}
260+
265261

266262
RequestPart requestPart = methodParameter.getParameterAnnotation(RequestPart.class);
267263
String paramName = null;
@@ -273,41 +269,38 @@ public void calculateRequestBodyInfo(Components components, MethodAttributes met
273269
paramName = StringUtils.defaultIfEmpty(paramName, parameterInfo.getpName());
274270
parameterInfo.setpName(paramName);
275271

276-
requestBody = buildRequestBody(requestBody, components, methodAttributes, parameterInfo,
272+
io.swagger.v3.oas.annotations.parameters.RequestBody requestBodyDoc = methodParameter.getParameterAnnotation(io.swagger.v3.oas.annotations.parameters.RequestBody.class);
273+
requestBody = buildRequestBody(requestBodyDoc, components, methodAttributes, parameterInfo,
277274
requestBodyInfo);
278275
requestBodyInfo.setRequestBody(requestBody);
279276
}
280277

281278
/**
282279
* Build request body.
283280
*
284-
* @param requestBody the request body
281+
* @param requestBodyDoc the request body doc
285282
* @param components the components
286283
* @param methodAttributes the method attributes
287284
* @param parameterInfo the parameter info
288285
* @param requestBodyInfo the request body info
289286
* @return the request body
290287
*/
291-
private RequestBody buildRequestBody(RequestBody requestBody, Components components,
288+
private RequestBody buildRequestBody(io.swagger.v3.oas.annotations.parameters.RequestBody requestBodyDoc, Components components,
292289
MethodAttributes methodAttributes,
293290
ParameterInfo parameterInfo, RequestBodyInfo requestBodyInfo) {
291+
RequestBody requestBody = requestBodyInfo.getRequestBody();
294292
if (requestBody == null) {
295293
requestBody = new RequestBody();
296294
requestBodyInfo.setRequestBody(requestBody);
297295
}
298296

299-
if (requestBody.getContent() == null) {
300-
Schema<?> schema = parameterBuilder.calculateSchema(components, parameterInfo, requestBodyInfo,
301-
methodAttributes.getJsonViewAnnotationForRequestBody());
302-
Map<String, Encoding> parameterEncoding = getParameterEncoding(parameterInfo);
303-
buildContent(requestBody, methodAttributes, schema, parameterEncoding);
304-
}
305-
else if (!methodAttributes.isWithResponseBodySchemaDoc()) {
306-
Schema<?> schema = parameterBuilder.calculateSchema(components, parameterInfo, requestBodyInfo,
307-
methodAttributes.getJsonViewAnnotationForRequestBody());
308-
Map<String, Encoding> parameterEncoding = getParameterEncoding(parameterInfo);
309-
mergeContent(requestBody, methodAttributes, schema, parameterEncoding);
310-
}
297+
if (requestBodyDoc != null)
298+
requestBody = this.buildRequestBodyFromDoc(requestBodyDoc, methodAttributes, components).orElse(requestBody);
299+
300+
Schema<?> schema = parameterBuilder.calculateSchema(components, parameterInfo, requestBodyInfo,
301+
methodAttributes.getJsonViewAnnotationForRequestBody());
302+
Map<String, Encoding> parameterEncoding = getParameterEncoding(parameterInfo);
303+
buildContent(requestBody, methodAttributes, schema, parameterEncoding);
311304

312305
// Add requestBody javadoc
313306
if (StringUtils.isBlank(requestBody.getDescription()) && parameterBuilder.getJavadocProvider() != null
@@ -320,18 +313,6 @@ else if (!methodAttributes.isWithResponseBodySchemaDoc()) {
320313
return requestBody;
321314
}
322315

323-
/**
324-
* Merge content.
325-
*
326-
* @param requestBody the request body
327-
* @param methodAttributes the method attributes
328-
* @param parameterEncoding the parameter encoding
329-
*/
330-
private void mergeContent(RequestBody requestBody, MethodAttributes methodAttributes, Schema<?> schema, Map<String, Encoding> parameterEncoding) {
331-
Content content = requestBody.getContent();
332-
buildContent(requestBody, methodAttributes, schema, content, parameterEncoding);
333-
}
334-
335316
/**
336317
* Build content.
337318
*
@@ -341,31 +322,27 @@ private void mergeContent(RequestBody requestBody, MethodAttributes methodAttrib
341322
* @param parameterEncoding the parameter encoding
342323
*/
343324
private void buildContent(RequestBody requestBody, MethodAttributes methodAttributes, Schema<?> schema, Map<String, Encoding> parameterEncoding) {
344-
Content content = new Content();
345-
buildContent(requestBody, methodAttributes, schema, content, parameterEncoding);
346-
}
325+
Content content;
326+
if (requestBody.getContent() == null) {
327+
content = new Content();
328+
}
329+
else {
330+
content = requestBody.getContent();
331+
}
347332

348-
/**
349-
* Build content.
350-
*
351-
* @param requestBody the request body
352-
* @param methodAttributes the method attributes
353-
* @param schema the schema
354-
* @param content the content
355-
* @param parameterEncoding the parameter encoding
356-
*/
357-
private void buildContent(RequestBody requestBody, MethodAttributes methodAttributes, Schema<?> schema, Content content, Map<String, Encoding> parameterEncoding) {
358333
for (String value : methodAttributes.getMethodConsumes()) {
359334
MediaType mediaTypeObject = new MediaType();
360-
mediaTypeObject.setSchema(schema);
361335
MediaType mediaType = content.get(value);
336+
mediaTypeObject.setSchema(schema);
362337
if (mediaType != null) {
363338
if (mediaType.getExample() != null)
364339
mediaTypeObject.setExample(mediaType.getExample());
365340
if (mediaType.getExamples() != null)
366341
mediaTypeObject.setExamples(mediaType.getExamples());
367342
if (mediaType.getEncoding() != null)
368343
mediaTypeObject.setEncoding(mediaType.getEncoding());
344+
if (mediaType.getSchema() != null)
345+
mediaTypeObject.setSchema(mediaType.getSchema());
369346
}
370347
else if (!CollectionUtils.isEmpty(parameterEncoding)) {
371348
mediaTypeObject.setEncoding(parameterEncoding);
@@ -392,7 +369,7 @@ private Map<String, Encoding> getParameterEncoding(ParameterInfo parameterInfo)
392369
}
393370
else {
394371
String encodingContentType = parameterContent.keySet().iterator().next();
395-
if(StringUtils.isNotBlank(encodingContentType)) {
372+
if (StringUtils.isNotBlank(encodingContentType)) {
396373
return Map.of(parameterInfo.getpName(), new Encoding().contentType(encodingContentType));
397374
}
398375
}

springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app119/HelloController.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import io.swagger.v3.oas.annotations.parameters.RequestBody;
3232

3333
import org.springframework.http.MediaType;
34+
import org.springframework.http.ResponseEntity;
3435
import org.springframework.web.bind.annotation.PostMapping;
3536
import org.springframework.web.bind.annotation.RequestPart;
3637
import org.springframework.web.bind.annotation.RestController;
@@ -54,4 +55,13 @@ public String multiFilesInMultiPart(
5455
@RequestPart(value = "file2", required = false) @Parameter(description = "This is file2") final MultipartFile file2) {
5556
return "Hello World " + jsonRequest.getName();
5657
}
58+
59+
@PostMapping(value = "/uploadFileWithJson2", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}, produces = {
60+
MediaType.APPLICATION_JSON_VALUE})
61+
public ResponseEntity<?> uploadFileWithJson(
62+
@Parameter(description="file") @RequestPart("file") final MultipartFile file,
63+
@RequestBody(description = "toto", content = @Content(encoding = @Encoding(name = "jsonRequest", contentType = MediaType.APPLICATION_JSON_VALUE)))
64+
@Parameter(description = "An extra JSON payload sent with file") @RequestPart("jsonRequest") final JsonRequest jsonRequest) {
65+
return null;
66+
}
5767
}

springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.1.0/app119.json

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,55 @@
1111
}
1212
],
1313
"paths": {
14+
"/uploadFileWithJson2": {
15+
"post": {
16+
"tags": [
17+
"hello-controller"
18+
],
19+
"operationId": "uploadFileWithJson",
20+
"requestBody": {
21+
"description": "toto",
22+
"content": {
23+
"multipart/form-data": {
24+
"schema": {
25+
"type": "object",
26+
"properties": {
27+
"file": {
28+
"type": "string",
29+
"format": "binary",
30+
"description": "file"
31+
},
32+
"jsonRequest": {
33+
"$ref": "#/components/schemas/JsonRequest"
34+
}
35+
},
36+
"required": [
37+
"file",
38+
"jsonRequest"
39+
]
40+
},
41+
"encoding": {
42+
"jsonRequest": {
43+
"contentType": "application/json"
44+
}
45+
}
46+
}
47+
}
48+
},
49+
"responses": {
50+
"200": {
51+
"description": "OK",
52+
"content": {
53+
"application/json": {
54+
"schema": {
55+
"type": "object"
56+
}
57+
}
58+
}
59+
}
60+
}
61+
}
62+
},
1463
"/multi": {
1564
"post": {
1665
"tags": [
@@ -22,25 +71,25 @@
2271
"content": {
2372
"multipart/form-data": {
2473
"schema": {
25-
"required": [
26-
"params"
27-
],
2874
"type": "object",
2975
"properties": {
76+
"params": {
77+
"$ref": "#/components/schemas/JsonRequest"
78+
},
3079
"file1": {
3180
"type": "string",
32-
"description": "This is file1",
33-
"format": "binary"
81+
"format": "binary",
82+
"description": "This is file1"
3483
},
3584
"file2": {
3685
"type": "string",
37-
"description": "This is file2",
38-
"format": "binary"
39-
},
40-
"params": {
41-
"$ref": "#/components/schemas/JsonRequest"
86+
"format": "binary",
87+
"description": "This is file2"
4288
}
43-
}
89+
},
90+
"required": [
91+
"params"
92+
]
4493
},
4594
"encoding": {
4695
"params": {
@@ -69,12 +118,12 @@
69118
"schemas": {
70119
"JsonRequest": {
71120
"type": "object",
121+
"description": "This is the configuration",
72122
"properties": {
73123
"name": {
74124
"type": "string"
75125
}
76-
},
77-
"description": "This is the configuration"
126+
}
78127
}
79128
}
80129
}

0 commit comments

Comments
 (0)