How to send MultiPart HTTP request using Spring RestTemplate - spring

I am new to Spring framework and i am learning.
My web applicaiton based out of Spring MVC needs to call a vendor service through RESTful interface.
I have current implementation for POST / GET for non multipart.
However i have a requirement to POST multipart form data consisting of JSON and Bytes
I am trying to see some smaple implementation online for reference but could not get one.
I need some idea about possible approach i can take to implement this using RestTemplate.
Thanks for reading.

if we want to send multipart form data in post request and with that if you also want some information in json format then you can create your REST call according to this. here #Consumes will say that this call will accept only MULTIPART_FORM_DATA, #Transactional is for starting new transaction.
Here i am accepting three path parameters which are
1) String jsonObj, this is a string or you can say json, in this JSON you can ask UI for required information like some tags, labels, etc
2) FormDataContentDisposition fileDetail, This will contain very basic detail of file, like fileName, contentType, etc
3) InputStream uploadedInputStream, this will contain some binary data, like image, videos, or any kind of files in byte format.
* Example *
#POST
#Path("/xyz")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public VObject postMultiPartFormData(
#FormDataParam("jsonObj") String jsonObj,
#FormDataParam("mmFile") FormDataContentDisposition fileDetail,
#FormDataParam("mmFile") InputStream uploadedInputStream) {
return new VObject();
}
I hope this will help you.

Related

How can I avoid deserealization in microservise?

I'm developing microservise which receives HTTP POST requests and then redirects them to another destination point in order to receive response, handle it and hand it to another system.
My question is - how can I avoid deserealization while receiving these requests? The microservice don't handle incoming JSONs. From the performance point of view, it's better to avoid deserealization.
I heard I can use Nginx or Apache but how can I embed them into the microservice?
Is there any proven solution? Just don't want to invent a bicycle.
I don't know what is the technology stack you are using (programming language, framework.. etc) but I suppose that in all of them is a way to recieve incoming rest requests without need to deserialize the body.
For example in a java/spring scenario you can define in the corresponding controller method the body parameter as string and in this case there isn't any deserialization of the body, the method will recieve it as a plain string so you can forward it as is to the other service:
#PostMapping(value = "/path")
public ResponseEntity controllerMethod(#RequestBody String body) {
// your code here
}

Scala MultipartEntityBuilder is not creating the boundary correctly for a Spring Rest point

I have a request body that consists of more than one body part and below is how I am handling it in scala using a multipartentitybuilder library in scala.
val data = MultipartEntityBuilder.create()
.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
.addPart("actual Header", new StringBody(actualHeader,ContentType.APPLICATION_JSON))
.addBinaryBody("payload", requestBody)
.build()
for context, the binary body is a Byte[] of an avro event that needs to be sent to this post request.
On Attempting to send this entity to a spring rest endpoint via httpPost, I am getting the following exception in response.
org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request; nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
What I have tried:
I tried to manually add a boundary with .setBoundary() but that did not help.
There was another way to go in the other solutions available online. I could get rid of the headers that say ContentType.Application-JSON, however removing that is going to fail at the API level, since they have checks for it, so that wouldn't work either.
I tried adding the binary body as a part to make it consistent which made the code look like this
val data = MultipartEntityBuilder.create()
.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
.addPart("Event Header", new StringBody(eventHeader,ContentType.APPLICATION_JSON))
.addPart("payload",new ByteArrayBody(requestBody, ContentType.DEFAULT_BINARY, null.asInstanceOf[String]))
.build()
Tried to have the multipartEntityBuilder create the textbody (for JSON part)
and bytearray for payload part and got rid of content Type, using the following code
val data = MultipartEntityBuilder.create()
.setBoundary(boundary)
.addTextBody("Event Header",eventHeader,ContentType.APPLICATION_JSON) .addBinaryBody("Payload",requestBody,ContentType.DEFAULT_BINARY,null.asInstanceOf[String])
.build()
this got me further as in it autodetected the ContentType as multipart/form-data but when I setEntity to post, it did not transfer that information over to the POST and that still didn't correctly assign the boundary as part of the content type in the header
Also tried to add header from a file making sure the two bodies were consistent in nature with the following code
val file = new File("eventHeader.json")
val data = MultipartEntityBuilder.create()
.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
.addBinaryBody("Event Header",file,ContentType.APPLICATION_JSON,"eventHeader.json")
.addBinaryBody("Avro Payload",requestBody)
.build()
No luck
For more context and info, below are the other attributes to the POST request
val post = new HttpPost(url);
val client: CloseableHttpClient = HttpClientBuilder.create().build();
post.setConfig(requestConfig)
post.setHeader("Content-type", "multipart/form-data")
post.setEntity(data)
val response: HttpResponse = client.execute(post)
Another piece of information, when I debug the entity in IntelliJ it shows me a boundary that is created but I don't see it anywhere physically on the content.(not sure how helpful this information is)
Now, the exception is referencing to springframework and I have a nagging suspicion that scala is not playing well with that. However, we have successfully implemented gatling test scenarios in scala so it is not completely impossible to pull that off.
Hence, I now surrender to the community's expertise on how to go about solving this without being too invasive because this is going to be part of a spark service so let's not ignore the data distribution aspect of it all.
I finally got it to work with a simple addition of boundary from the content type of the data originally generated.
Below is how I changed my code for the request to go through,
val file = new File("testHeader.json")
val data = MultipartEntityBuilder.create()
.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
.addBinaryBody("Event Header",file,ContentType.APPLICATION_JSON,"testHeader.json")
.addBinaryBody("Avro Payload",requestBody)
.build()
val client: CloseableHttpClient = HttpClientBuilder.create().build();
val APIUrl = url
val post = new HttpPost(APIUrl);
post.setConfig(requestConfig)
post.setHeader(data.getContentType)
post.setEntity(data)
Simple solution but kind of a work around, for some reason (that I am not aware of) it didn't auto detect the type when I didn't set the content type at all. Hopefully this helps another poor dud like me sometime in the future.

Get part of JSON response object with Spring RestTemplate

I am trying to consume this, by following the tutorial from http://spring.io/guides/gs/consuming-rest/.
And I am interested in the "feeds" part of the response only.
My question is:
Is there any other way without using the toString() method in the tutorial. As, I want to access the some parts of response body and display it on a webpage.
Please, provide me with the details of the model and controller.

Spring REST API with swagger - map of values in request param

I have a Spring Boot based REST API application with the following endpoint (Written in Kotlin)
#RequestMapping(value = ["/search"], method = [RequestMethod.GET])
#ApiOperation("Check whether any of the map values exists. Returns string 'true' if stamp exists, else 'false'")
fun checkExists(
#ApiParam("information about the stamp as key-value pairs (example: ds=2017-11-34&hh=05)", required = true)
#RequestParam searchValues: Map<String, String>
): Boolean {
return service.checkExists(searchValues)
}
And I know Spring supports sending a dynamic map of key value pairs as documented here.
I am also using Swagger to document the API definitions, and further more, I am using swagger-codegen-cli to generate the client library using which someone can connect with this REST API.
Now, the issue is, I am not able to send a map of values from the swagger generated client to the Spring REST API (even though Spring supports it). Starting from Swagger OpenAPI 3, they've added support for Object types in the specification. But this works in a different way than I need. For example with just Spring and RequestParam of type Map
http://localhost:8080/search?foo=A&bar=B
is parsed as a map of key value pairs
key="foo",value="A"
key="bar",value="B"
But, When I send a Map Object from the swagger client with the same key-value pairs
Map<String, String> values = new HashMap<>();
values.put("foo","A");
values.put("bar","B");
return out = clientApi.checkExistsUsingGET(values);
This sends a request to the REST API in form of
http://localhost:8080/search?searchValues={foo=A,bar=B}
and the map in Spring side ends up as
key="searchValues",value="{foo=A,bar=B}"
I've been struggling to get the swagger client api to send the request in a way the Spring API is intended to work with Map of values, but I am not able to figure a solution.
Am I doing using the client API in a wrong way?, or this just can't be done with swagger?
Any suggestions/opinions appreciated!
This is not yet supported by swagger-ui. See this issue https://github.com/swagger-api/swagger-ui/issues/2241

How to define the return type of #RestControllerAdvice in Spring Boot 1.5

We are building a RESTful API with several #RestControllers which return all kinds of objects including byte arrays (actually application/pdf).
When an exception occurs we handle those with #RestControllerAdvices which return a custom ErrorView object. Still spring insists of rendering those as application/pdf which of course is not possible. The client is sending application/json in the accept header but this does not seem to help. Any pointers how to fix this?
Ok, I made a mistake which I think I should share.
Basically most of the time the content type negotiation between Spring and the client works like a charm. If the client accepts (as is the case for our app) "application/pdf, application/json" then spring will try to work it our for all responses AKA return values from #RestController functions. Except, of course, when you explicitly tell Spring to only produce a certain type of response e.g. with
#GetMapping(value = "render/document/{docId}", produces = arrayOf(MediaType.APPLICATION_PDF_VALUE))
After changing this to
#GetMapping(value = "render/document/{docId}", produces = arrayOf(MediaType.APPLICATION_PDF_VALUE, MediaType.APPLICATION_JSON_VALUE))
the ErrorView gets rendered as JSON as intended.

Resources