When generating multipart interfaces, the interface code is not working for Spring with Jersey:
Actual result:
ResponseEntity<Void> upload(
@ApiParam(value = "", required=true, defaultValue="null")
@RequestParam(value="meta", required=true) MetaData meta,
@ApiParam(value = "file detail") @Valid
@RequestPart("file") MultipartFile document) {
This code line leads to the following error on invocation:
Failed to convert value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile'
to required type 'demo.oagen.fileupload.spring.gen.MetaData'; nested exception is
java.lang.IllegalStateException: Cannot convert value of type
'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile'
to required type 'demo.oagen.fileupload.spring.gen.MetaData':
no matching editors or conversion strategy found
Expected result:
ResponseEntity<Void> upload(
@ApiParam(value = "", required=true, defaultValue="null")
@RequestPart(value="meta", required=true) MetaData meta,
@ApiParam(value = "file detail") @Valid
@RequestPart(value = "document", required=true) MultipartFile document) {
3.3.4
Example interface extract, full example see attached:
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
meta:
$ref: "#/components/schemas/MetaData"
document:
type: string
format: binary
required:
- meta
- document
Maven code:
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>3.3.4</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/demo.yaml</inputSpec>
<generatorName>spring</generatorName>
<output>${project.basedir}/target</output>
<apiPackage>demo.oagen.fileupload.spring.gen</apiPackage>
<modelPackage>demo.oagen.fileupload.spring.gen</modelPackage>
<configOptions>
<sourceFolder>/generated-sources/java</sourceFolder>
<basePackage>demo.oagen.fileupload.spring.gen</basePackage>
<configPackage>demo.oagen.fileupload.spring.gen.config</configPackage>
<useTags>true</useTags>
<interfaceOnly>true</interfaceOnly>
</configOptions>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>3.3.4</version>
<type>maven-plugin</type>
</dependency>
</dependencies>
</plugin>
Execute code generation via Maven on example project attached.
oagen-fileupload-spring-demo.zip
Send the following request:
POST http://127.0.0.1:8080/upload HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: multipart/form-data; boundary="----=_Part_17_744410358.1544434419692"
MIME-Version: 1.0
Content-Length: 82439
Host: 127.0.0.1:8080
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.5.2 (Java/1.8.0_162)
------=_Part_17_744410358.1544434419692
Content-Type: application/json; name=demo-meta.json
Content-Transfer-Encoding: binary
Content-Disposition: form-data; name="meta"; filename="demo-meta.json"
{
"fileName": "testfile.pdf",
"comment": "hello"
}
------=_Part_17_744410358.1544434419692
Content-Type: application/pdf; name=Test.pdf
Content-Transfer-Encoding: binary
Content-Disposition: form-data; name="document"; filename="Test.pdf"
(binary content here)
Note: The example pom.xml contains a workaround for replacing the concerned code line with the correct code.
this is still a bug in version 4.2.2 of the generator and multiple files (specified as an array of binary strings) don't produce a list of multiPartFiles. Swagger generator does a better job generating a list of Resource objects, respecting the "required" indications and using the names provided in the yaml for the parts
Hello,
I had the exact same problem, but had the particularity to use a forked version of openapi-generator. We switched to the official version 4.3.1 and it looks ok to me :
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
myObject:
$ref: '#/components/schemas/MyObject'
file:
type: string
format: binary
required:
- myObject
- file
The corresponding java generation :
@RequestMapping(value = "/clients/{client_id}/accounts/{account_id}/myEndpoint",
produces = { "application/json" },
consumes = { "multipart/form-data" },
method = RequestMethod.POST)
ResponseEntity<DossierGestionReponse> myEndpoint(
@ApiParam(value = "Client id",required=true) @PathVariable("client_id") String clientId,
@ApiParam(value = "Account id",required=true) @PathVariable("account_id") String accountId,
@ApiParam(value = "", required=true, defaultValue="null") @RequestPart(value="myObject", required=true) MyObject myObject,
@ApiParam(value = "") @Valid @RequestPart(value = "file") MultipartFile file);
We also struggled to test this with postman. we need to specify explicitely the content type of "myObject" like this (Otherwise we get an error 415) :

And do the same in our tests :
// Given
MockPart fileMock = new MockPart("file", "file", "test data".getBytes(StandardCharsets.UTF_8));
MockPart myObjectMock = new MockPart("myObject", "myObject", objectMapper.writeValueAsString(myObject).getBytes(StandardCharsets.UTF_8));
myObjectMock.getHeaders().setContentType(MediaType.APPLICATION_JSON); // Set the content type explicitely in form-data for json objects
//WHEN
MvcResult mvcResult = this.mockMvc.perform(
multipart("/clients/" + DEFAULT_CLIENT_ID + "/accounts/" + DEFAULT_ACCOUNT_ID + "/myEndpoint")
.part(fileMock)
.part(myObjectMock))
.andDo(print())
.andExpect(status().isOk())
.andReturn();
Hope this helps
Most helpful comment
this is still a bug in version 4.2.2 of the generator and multiple files (specified as an array of binary strings) don't produce a list of multiPartFiles. Swagger generator does a better job generating a list of Resource objects, respecting the "required" indications and using the names provided in the yaml for the parts