Can not upload file when using a swagger generated spring server - java

I want to implement a file readout function on my REST Service. Since I do not know how to use spring myself, I use swagger to generate the server code for me. Normally this works perfectly fine, but when I try to upload files I get the following error:
{
"timestamp": "2018-11-07T12:27:43.119Z",
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.web.multipart.support.MissingServletRequestPartException",
"message": "Required request part 'file' is not present",
"path": "/requirements/import"
}
My yaml uses the following lines for the import function:
/requirements/import:
post:
consumes:
- multipart/form-data
description:
Returns all requirements contained in the submitted reqIf file.
parameters:
- name: reqIfFile
in: formData
type: file
description: The reqIf file that contains the requirements.
responses:
200:
description: An array of requirements.
schema:
type: array
items:
$ref: 'requirement'
The generated interface (with some added exceptions):
#javax.annotation.Generated(value = "io.swagger.codegen.languages.SpringCodegen", date = "2018-04-05T07:19:00.887Z")
#Api(value = "requirements", description = "the requirements API")
public interface RequirementsApi {
#ApiOperation(value = "", nickname = "requirementsImportPost", notes = "Returns all requirements contained in the submitted reqIf file.", response = Requirement.class, responseContainer = "List", tags = {})
#ApiResponses(value = {
#ApiResponse(code = 200, message = "An array of requirements.", response = Requirement.class, responseContainer = "List") })
#CrossOrigin(origins = "*")
#RequestMapping(value = "/requirements/import", produces = { "application/json" }, consumes = {
"multipart/form-data" }, method = RequestMethod.POST)
ResponseEntity<List<Requirement>> requirementsImportPost(
#ApiParam(value = "file detail") #Valid #RequestPart("file") MultipartFile reqIfFile)
throws IOException, ContinuumException;
}
The code that actually does the readout:
#javax.annotation.Generated(value = "io.swagger.codegen.languages.SpringCodegen", date = "2018-04-05T07:19:00.887Z")
#Controller
public class RequirementsApiController implements RequirementsApi {
#Override
public ResponseEntity<List<Requirement>> requirementsImportPost(
#ApiParam(value = "file detail") #Valid #RequestPart("file") final MultipartFile reqIfFile)
throws IOException, ContinuumException {
InputStream fileStream = new BufferedInputStream(reqIfFile.getInputStream());
List<Requirement> list = ReadReqIF.readReqIfFile(fileStream);
return new ResponseEntity<List<Requirement>>(list, HttpStatus.OK);
}
}
Can someone tell me where a possible error is?

I encountered the same problem with my swagger generated spring server.
I was able to workaround the problem by modifying the generated server code to change the name "file" in #RequestPart("file") to the name specified in the swagger spec. In your case, it should be #RequestPart("reqIfFile"). It'd have to be modified in both the interface and controller code.
There is likely a bug in the Spring server generator code in Swagger editor. I can't think of any other reason for the RequestPart annotation to be named "file" which is essentially the "type" and not name of the parameter.

Related

Spring Boot Unsupported Media Type (415) for file uploads

I'm trying to create a new POST endpoint using Spring Boot using the following code:
#Controller
#Path("/my")
#MultipartConfig(maxFileSize = 1024*1024*1024, maxRequestSize = 1024*1024*1024)
public class MyResource {
#POST
#Path("parseFile")
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = "OK"),
#ApiResponse(responseCode = "400", description = "Invalid format")})
})
public Response parseFile(#RequestParam("file") MultipartFile file) {
// Use file
}
}
I've added config in application.yml file:
spring:
servlet:
multipart:
enabled: true
max-file-size: 2MB
file-size-threshold: 3MB
Based on the docs here, it should automagically work and allow requests, but I'm getting the following response:
{
"timestamp": 1667463311931,
"status": 415,
"error": "Unsupported Media Type",
"path": "/app/api/my/parseFile"
}
I've also tried adding AutoConfig elements manually as well in a #Configuration class like:
#Bean
public MultipartAutoConfiguration multipartAutoConfiguration() {
var props = new MultipartProperties();
props.setMaxFileSize(DataSize.ofMegabytes(10));
props.setEnabled(true);
return new MultipartAutoConfiguration(props);
}
On the server side I'm only seeing the following log:
{"#timestamp":"2022-11-03T08:10:34.066Z","message":"0:0:0:0:0:0:0:1 - - [03/Nov/2022:08:10:34 +0000] \"POST /app/api/my/parseFile HTTP/1.1\" 415 126 \"-\" \"PostmanRuntime/7.29.2\"","request_id":"-","local_request_id":"4f9396ff817861e9","ext":{"accessLog":true,"cloudId":"fake","host":"0:0:0:0:0:0:0:1","method":"POST","protocol":"HTTP/1.1","statusCode":"415","requestedUri":"/app/api/my/parseFile","requestPath":"/app/api/my/parseFile","responseContentLength":"126","elapsedTimeMs":"5"}}
Postman Request:
The file type that I'm trying to upload is of type *.ics and is a text file.
I'm using Spring Boot version 2.5.2.
The error says that the media type header you send is not supported. I'm not 100% familiar #POST but I guess there is a possibility there as well.
In any cases, you can use Spring annotations. Change it from #POST and #Path to #PostMapping(path = "parseFile", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE })
change your post method to this
#PostMapping(value="/parsefile", consumes ="multipart/form-data")
public Response parseFile(#RequestParam(value = "file") MultipartFile file)
{
// Use file
}

Spring Rest Controller with HttpServletRequest for upload resulting in no input file upload in Swagger

I have spring rest controller with some parameters and HttpServletRequest in input. I use it to stream input file. Simplifying:
#PostMapping(path = "...", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
#Operation(summary = "Create document", description = "upload file for creating a document")
#ApiResponse(content = #Content(mediaType = MediaType.TEXT_PLAIN_VALUE, schema = #Schema(implementation = MyDTO.class)))
#ApiResponses(value = { #ApiResponse(responseCode = "200", description = "Data found"),
#ApiResponse(responseCode = "400", description = "Request not compliant with the defined schema")
...
public ResponseEntity<MyDTO> myUpl(
#PathVariable("myParam1") Long param1,
#RequestParam(name = "myParam2", required = true) Integer myParam2,
HttpServletRequest request ) {
So when I invoke swagger-ui and produce swagger yaml there is no file input.
The code works perfectly.
I need to clarify in swagger the input file for frontend team.
How can I do that?
EDIT:
Following other posts, temporanely added to controller:
#RequestBody(description = "Input file",
content = #Content(mediaType = MediaType.MULTIPART_FORM_DATA_VALUE,
schema = #Schema(implementation = MultipartRequest.class),
encoding = #Encoding(name = "file", contentType = "application/pdf")))
At least I have in swagger yaml:
requestBody:
description: Input file
content:
multipart/form-data:
schema:
$ref: '#/components/schemas/MultipartRequest'
encoding:
file:
contentType: application/pdf
and un swagger-ui:
Request body multipart/form-data
Input file
fileMap
object
fileNames
object
multiFileMap
object
It's the right way? There's something better? Is it possible to show Input File button in swagger-ui?

How to return different objects for different response codes in Swagger-generated REST API?

I want to respond with different result objects in a Swagger generated API. The type of object is dependent on the result code.
But it seems that the Swagger codegen generates only code that allows the first defined/used type to be returned.
An example Swagger definition that returns different objects in the OK and error case is like:
swagger: "2.0"
info:
description: "API"
version: 1.0.0
title: Example
host: localhost:8080
schemes:
- http
paths:
/exampleCall:
get:
operationId: exampleCall
produces:
- application/json
responses:
200:
description: OK
schema:
$ref: '#/definitions/exampleResponse'
400:
description: Error
schema:
$ref: '#/definitions/exampleError'
definitions:
exampleResponse:
type: object
properties:
result:
type: string
exampleError:
type: object
properties:
code:
type: string
This then gets generated by the SwaggerCodeGen into following API interface
#Validated
#Api(value = "exampleCall", description = "the exampleCall API")
#RequestMapping(value = "")
public interface ExampleCallApi {
ExampleCallApiDelegate getDelegate();
#ApiOperation(value = "", nickname = "exampleCall", notes = "", response = ExampleResponse.class, tags={ })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "OK", response = ExampleResponse.class),
#ApiResponse(code = 400, message = "Error", response = ExampleError.class) })
#RequestMapping(value = "/exampleCall",
produces = { "application/json" },
method = RequestMethod.GET)
default ResponseEntity<ExampleResponse> exampleCall() {
return getDelegate().exampleCall();
}
}
But when I try to implement the delegate like this
public class ExampleCallApiDelegateImpl implements ExampleCallApiDelegate {
#Override
public ResponseEntity<ExampleResponse> exampleCall() {
ExampleError error = new ExampleError();
error.setCode("123");
return new ResponseEntity<ExampleError>(error, HttpStatus.BAD_REQUEST);
}
}
it of course fails to compile because of incorrect return types.
What would be the proper way to implement different return objects per response code with that Swagger generated API?
Is there even a proper way?

How to rethrown an exception in Java

I am using maven codegen plugin to generate the controller interface with a schema like the following
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/MyResponse'
description: OK
'401':
content:
application/json:
schema:
$ref: '#/components/schemas/MyError'
The interface like the following
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = "Authentication succeeded", content = #Content(mediaType = "application/json", schema = #Schema(implementation = MyResponse.class))),
#ApiResponse(responseCode = "401", description = "Authentication failed", content = #Content(mediaType = "application/json", schema = #Schema(implementation = MyError.class))) })
#RequestMapping(value = "/login", method = RequestMethod.POST)
default ResponseEntity<MyResponse> LoginMethod(//some parameters...) { //something}
In my controller, I would like to call an external API which throws an API exception
public ResponseEntity<MyResponse> LoginMethod(//some parameters...) {
try {
//call external API which throw an exception
} catch(ApiException e){
e.getResponseBody; // This is a string type of MyError class in JSON format returned
// throw e;
}
I would like to redirect the response body but the interface defines the return type to be ResponseEntity so I can't simply rethrow the exception or return ResponseEntity.
#ApiResponse seems not correcting the response type as well.
As stated in this question,
How to handle multiple response/return types (empty for 204, non-empty for 400 etc) in swagger codegen?
I can throw as this way
throw new ResponseStatusException(HttpStatus.valueOf(e.getCode()), e.getResponseBody());
But is there a better way to do that? I just want to return the e.getResponseBody() as an object instead of a string.
Many thanks.
You can add the ApiException in throws declaration like that :
public ResponseEntity<MyResponse> LoginMethod(//some parameters...) throws ApiException {
// here your code that can create teh ApiException
}
Now the method which call this will ask for the throw exception too. You will be able to manage exception in it.
You can also create a new object which contains all informations that you need. It will also format informations to be always the same, not depending of the throwed error.

Does #RequestBody annotation works with the class having private constructor. I'm using Springboot application

I am using #RequestBody annotation in my controller on a class which is from maven dependency library and it has private constructor.
#RequestMapping(value = "/myApi", method = RequestMethod.POST, produces = "application/json; charset=utf-8")
#ApiOperation(value = "My API", response = CustomResponse.class, notes = "API response beautified")
public String apiOperation( #RequestBody #ApiParam(value = "ReqBody",required = true)
MyAPIBody apiReqBody ) {
// some code
// MyAPIBody is imported from maven dependency lib
// and has the all constructors as private
}
I am getting 415 error.
{
"timestamp": "2019-02-03T19:26:30.738+0000",
"status": 415,
"error": "Unsupported Media Type",
"message": "Content type 'application/json;charset=UTF-8' not supported",
}
Can someone suggest what I am doing incorrectly, or i require some change in my project structure .
I feel that it(class with private constructor) should not work with requestbody, because when I applied the RequestBody param to some other class, it worked. But, if someone could explain the reason, i could make well thought changes in my project structure. Or correct me if my feeling is incorrect.

Categories