OpenApi send MultipartFile request with JSON get 'application / octet-stream' error not supported - spring-boot

I'm using Spring Boot and I want send MultipartFile with json using Swagger UI but I receive the error 'application/octet-stream' error not supported,if I use Postman work very well.
#RequestMapping(value = "/upload", method = RequestMethod.POST,
produces = { "application/json" },
consumes = { "multipart/form-data" })
public String hello(
#RequestPart(value = "file") MultipartFile file,
#RequestPart("grupo") Grupo grupo) {
if (file != null) {
logger.info("File name: " + file.getOriginalFilename());
}
logger.info(grupo.toString());
return grupo.toString();
}
How to resolve this?
springdoc-openapi-ui 1.4.4
Spring Boot 2.3.2.RELEASE
spring-boot-starter-web
Maven
spring-boot-starter-data-jpa

To send a json with multipartFile, use the annotation #Parameter with type "string" and format "binary", so that you can send a file with format json.
#Parameter(schema =#Schema(type = "string", format = "binary"))
And then it will be like this.
#PostMapping(value = "/test", consumes = MediaType.MULTIPART_FORM_DATA_VALUE )
public ResponseEntity<Void> saveDocu2ment(
#RequestPart(value = "personDTO") #Parameter(schema =#Schema(type = "string", format = "binary")) final PersonDTO personDTO,
#RequestPart(value = "file") final MultipartFile file) {
return null;
}
Reference - Multipart Request with JSON - GitHub Springdoc openApi

Related

Getting null body in response from feign client, even though a direct request is returning an entity

I have this Feign Client in my spring boot application :
#Component
#FeignClient(value = "apiKeyManager", url = "http://localhost:8081/", configuration = FileUploadConfiguration.class)
public interface ApiKeyClient {
#RequestMapping(method = RequestMethod.POST, value = "/api/public/getAppName", consumes = "application/json", produces = "application/json")
ResponseEntity getAppName(#RequestBody AppNameRequestDto appNameRequestDto);
}
And I have this code in my service, which calls it :
AppNameRequestDto request = new AppNameRequestDto(apiKey);
ResponseEntity verification = apiKeyClient.getAppName(request);
return verification;
The actual endpoint being called by the feign client looks like this :
#PostMapping(value = "getAppName", consumes = "application/json", produces = "application/json")
public ResponseEntity getAppName(#RequestBody AppNameRequestDto appNameRequestDto){
try {
return new ResponseEntity(apiKeyManagementService.getAppName(appNameRequestDto.getApiKey()), HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity("Failed to locate application by API_KEY : " + appNameRequestDto.getApiKey(), HttpStatus.NOT_FOUND);
}
}
When I run this code - I get this response :
{
"headers": {
<REMOVED FOR BREVITY>
},
"body": null,
"statusCode": "OK",
"statusCodeValue": 200
}
But when I make the call to the underlying API directly, I get the response I am expecting - an entity with an accompanies 200 status :
{
"applicationName": "MyNewFileUploadServiceApplication6"
}

Netflix Feign Code Generation using Swagger

I found the project https://github.com/swagger-api/swagger-codegen .
However this is generating a client that is based on OpenFeign.
Is there a way to generate a client interface automatically that uses Netflix's feign annotation with request mappings?
Example:
#FeignClient(name = "ldap-proxy")
public interface LdapProxyClient {
#RequestMapping(path = "/ldap-proxy/v1/users/{userNameOrEMail}", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
LdapUser search(#PathVariable("userNameOrEMail") String userNameOrEMail);
}
As opposed to the class at:
https://github.com/swagger-api/swagger-codegen/blob/master/samples/client/petstore/java/feign/src/main/java/io/swagger/client/ApiClient.java
Thannks
You can try spring-cloud swagger-codegen library.
The example of the command to generate the client:
java -jar modules/swagger-codegen-cli/target/swagger-codegen-cli.jar generate \
-i http://petstore.swagger.io/v2/swagger.json \
-l spring \
--library spring-cloud \
-o samples/client/petstore/java
Here is the example of the generated files:
PetApi.java
#Api(value = "Pet", description = "the Pet API")
public interface PetApi {
#ApiOperation(value = "Add a new pet to the store", nickname = "addPet", notes = "", authorizations = {
#Authorization(value = "petstore_auth", scopes = {
#AuthorizationScope(scope = "write:pets", description = "modify pets in your account"),
#AuthorizationScope(scope = "read:pets", description = "read your pets")
})
}, tags={ "pet", })
#ApiResponses(value = {
#ApiResponse(code = 405, message = "Invalid input") })
#RequestMapping(value = "/pet",
produces = "application/json",
consumes = "application/json",
method = RequestMethod.POST)
ResponseEntity<Void> addPet(#ApiParam(value = "Pet object that needs to be added to the store" ,required=true ) #Valid #RequestBody Pet body);
}
PetApiClient.java
#FeignClient(name="${swaggerPetstore.name:swaggerPetstore}", url="${swaggerPetstore.url:http://petstore.swagger.io/v2}", configuration = ClientConfiguration.class)
public interface PetApiClient extends PetApi {
}

Sending file to Spring Boot REST using Axios

I am trying to send a csv file to my java spring boot backend. The code to send my file is below:
var url = 'http://localhost:3001/UploadFile';
var file = this.state.file;
var formData = new FormData();
formData.append("file", file);
axios.post(url, formData, {
headers: { 'Content-Type': 'multipart/form-data' }
});
And the code to accept my file from Spring Boot:
#CrossOrigin
#RequestMapping("/UploadFile")
#ResponseBody
public void uploadFile(#RequestParam("file") MultipartFile file) {
}
However, it doesn't seem to work. I keep getting an error saying that the 'Current request is not a multipart request'. Any ideas?
It's not sufficient to specify content-type in frontend you need to do it in controller as well.
You should tell to spring controller what it should consume and also it would be nice to set RequestMethod as POST like this:
#CrossOrigin
#RequestMapping("/UploadFile")
#ResponseBody
public void uploadFile(#RequestParam("file") MultipartFile file, method = RequestMethod.POST, consumes = "multipart/form-data") {
}

Angular 4 and Spring Rest: How to post FormData containing File and model object in a single request

I would like to send a File object along with custom model object in a single request.
let formData:FormData = new FormData();
let file = this.fileList[0];
formData.append('file', file, file.name);
formData.append('address', JSON.stringify(customObj));
...
this.http.post(fileServeUrl, formData)
My backend is in Spring Rest as below
#RequestMapping(value = "/fileServe",
produces = {"application/json"},
consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE},
method = RequestMethod.POST)
ResponseEntity<Image> uploadFile(#RequestPart("file") MultipartFile imageData, #RequestPart("address") Address address) throws IOException {...}
I was able to receive the data if I pass simple String along with File though.
formData.append('file', file, file.name);
formData.append('address', addressText);
Backend
#RequestMapping(value = "/fileServe",
produces = {"application/json"},
consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE},
method = RequestMethod.POST)
ResponseEntity<Image> uploadFile(#RequestPart("file") MultipartFile imageData, #RequestPart("address") String addressText) throws IOException {...}
I tried #RequestBody for my custom object but even that didn't work. Any advise please.
The problem with #Requestbody and #RequestPart annotation is that spring use the HttpMessageConverter to take convert the incoming json message into the your object. As you send form data with a file and a text value spring can not convert it into your object. I am afraid you have to pass the value of address seperatetly.
#RequestMapping(value = "/fileupload", headers = ("content-type=multipart/*"), method = RequestMethod.POST)
public ResponseEntity<AjaxResponseBody> upload(#RequestParam("file") MultipartFile file, #RequestParam String name, #RequestParam String postCode) {
AjaxResponseBody result = new AjaxResponseBody();
HttpHeaders headers = new HttpHeaders();
if (!file.isEmpty()) {
try {
Address address = new Address();
address.setName(name);
result.setMsg("ok");
return new ResponseEntity<AjaxResponseBody>(result, headers, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<AjaxResponseBody>(HttpStatus.BAD_REQUEST);
}
} else {
return new ResponseEntity<AjaxResponseBody>(HttpStatus.BAD_REQUEST);
}
}
Expept if you find a way your client app send a file with MimeType of image/jpg and and an address of application/json which allow spring to parse the json and map to your Address object which i couldn't do it.

Different encoding of an HTTP request result, depending on the Accept header

I have a controller with a method to upload files, using, on the client side, the dojo Uploader class that supports ajax uploads for all browsers except IE, and uploads with an IFrame for IE.
The result is a JSON object, but when the IFrame mechanism is used, the JSON must be enclosed in a <textarea>:
#RequestMapping(value = "/documentation/{appId:.+}/", method = RequestMethod.POST)
#ResponseBody
public String uploadDocumentation(HttpServletRequest request,
#PathVariable String appId, #RequestParam("uploadedfile") MultipartFile file)
throws Exception {
// ....
String json = JsonUtils.jsonify(map);
if (accepts(request, "application/json")) {
return json;
} else if (accepts(request, "text/html")) {
return "<textarea>" + json + "</textarea>";
} else {
throw new GinaException("Type de retour non supporté");
}
I was wondering if there is a way to register this encoding mechanism in the framework, so that we would just have to return an object, and let the framework do the rest.
Thanks in advance.
For the record, I simply added a second method:
#RequestMapping(value = "/documentation/{appId:.+}/", method = RequestMethod.POST,
produces="application/json")
#ResponseBody
public UploadResult uploadDocumentation(#PathVariable String appId,
#RequestParam("uploadedfile") MultipartFile file) throws Exception {
...
return new UploadResult(filename);
}
#RequestMapping(value = "/documentation/{appId:.+}/", method = RequestMethod.POST,
produces="text/html")
#ResponseBody
public String uploadDocumentationIE(#PathVariable String appId,
#RequestParam("uploadedfile") MultipartFile file) throws Exception {
UploadResult obj = uploadDocumentation(appId, file);
String json = JsonUtils.jsonify(obj);
return "<textarea>" + json + "</textarea>";
}

Resources