RestTemplate & multipart/form-data response - java

I need to write a REST Client using RestTemplate that will call the following endpoint:
#RequestMapping(value = "/{documentID}", method = RequestMethod.GET, produces = "multipart/form-data")
#ResponseBody
ResponseEntity<MultiValueMap<String, Object>> getDocument(#PathVariable("documentID") long documentID);
This endpoint builds multipart/form-data response including a document (InputStreamResource) and the document's info (JSON) parts.
However, I receive the following exception:
org.springframework.web.client.UnknownContentTypeException: Could not extract response: no suitable HttpMessageConverter found for response type [interface org.springframework.util.MultiValueMap] and content type [multipart/form-data;boundary=f9yLuCpxZoS4W5lu5iYivlD8fIo28BBMr5PXzu;charset=UTF-8]
I have FormHttpMessageConverter (that is supposed to process form data to/from a MultiValueMap) in my RestTemplate, but it still doesn't work because according to the official docs this converter can't read multipart/form-data (only write):
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/converter/FormHttpMessageConverter.html
This endpoint works fine via Postman, returning both JSON and File parts, so I'm wondering which kind of magic I'm missing to make it work using RestTemplate.
Is it possible to write a REST client to process multipart/form-data response and if yes, which converter should be used for such messages, do I have to write a custom HttpMessageConverter?

I collided with the same case and wrote a simple example(MultipartMessageConverter). This example allows to convert a request(resource and JSON) to one DTO model as you can se in test. A model can consist Resource
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MultiPartMessageConverter(objectMapper));
final ResponseEntity<ResultModel> responseEntity = restTemplate.getForEntity("http://localhost:" + randomServerPort + "/test", ResultModel.class);
final ResultModel resultModel = responseEntity.getBody();
Assertions.assertNotNull(resultModel);
Assertions.assertEquals(2, resultModel.secondModel.size());
Assertions.assertEquals("Param1.Value", resultModel.firstModel.param1);
Assertions.assertEquals("Param2.Value", resultModel.firstModel.param2);

Related

How do I upload a file and pass it to another service without writing it to the file system in Spring Boot?

Suppose I have a front end. On said front end, I want to upload a file. The controller in the Spring Boot (Java) app receives said file as a MultipartFile (Service A). I want to take the input stream from that, and send it to a different service (Service B) without writing said stream to the file system. Service B should return something to Service A which sends said response to the client to let me know it has handled said file after completion of the streaming. I am unsure which libraries/classes to use to achieve this in a Spring Boot app. I assume Java or Spring Boot has all kinds of helper classes to make this trivial, I'm just unsure which and how to string them together in the correct order.
Client --> Service A --> Service B
Any help would be appreciated, as the current approach of writing it to a file system is a horrible approach that I want refactored ASAP.
#Autowired
RestTemplate restTemplate
public customResponse myFileUpload(#RequestParam("foo") MultipartFile myFile) {
//myFile comes in fine, I can pull input stream via myFile.getInputStream();
//Should pull stream from myFile and return response from Service A here.
//Not sure if I need to map the input to an outputStream or something?
return restTemplate.postForObject(serviceA.url, ???, customResponse.class);
}
Using RestTemplate you can send Multipart Form Data requests. Below you can find a code snippet on how to prepare such requests.
Also RestTemplate allows you to upload Resource, which you can obtain from the MultipartFile by calling MultipartFile#getResource()
public String myFileUpload(#RequestParam("foo") MultipartFile myFile) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("bar", myFile.getResource());
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
ResponseEntity<String> responseEntity = restTemplate.postForEntity(
"http://localhost:8080/serviceB", // <-- put real endpoint here
requestEntity,
String.class
);
return responseEntity.getBody();
}

How to set value to #RequestAttribute in Spring boot using postman or feign client

I have method like this:
#PostMapping(path = "/workflow-services/{service_id}/tickets",
consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<TicketIdResponse> createTicket(#PathVariable("service_id") String serviceId,
#RequestBody #Validated CreateTicketRequest request, #RequestAttribute Payload payload) {
log.info("Start create ticket [{}]", request);
TicketIdResponse response = ticketService.createTicket(serviceId, request, payload);
log.info("Create ticket response: {}", response);
return ResponseFactory.success(response);
}
so how to set value to #RequestAttribute Payload in postman or feign client
Thank you very much!
The #RequestAttribute annotation is usually used to retrieve data that is populated on the server-side but during the same HTTP request. For example, if you have used an interceptor, filter or possibly an aspect to populate the "payload" attribute then you should be able to access this using the #RequestAttribute annotation.
If you are looking to pass something from an external client (i.e via postman, curl or any other simple client) - #RequestAttribute is not the way forward.
Good references;
https://www.baeldung.com/whats-new-in-spring-4-3
https://www.logicbig.com/tutorials/spring-framework/spring-web-mvc/request-attribute.html
This SO post may also help.

Spring rest template get for entity giving 400 error

I'm getting the following exception org.springframework.web.client.HttpClientErrorException: 400 Bad Request
on this statement final ResponseEntity<String> entity = restTemplate.getForEntity("http://localhost:12001/api/profiles/bymsisdn/0747894146", String.class);
But I'm getting successful response using
curl url -X GET "http://localhost:8001/api/profiles/bymsisdn/0747894146"
Extra notes:
resttemplate = new RestTemplate(); // No Headers or extra convertors added as I think it's not required because using curl works fine
You can use requestbin to test how your http client performs.
I think RestTemplate is putting Accept header for which your server responds 400. So you may need to edit your request headers:
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<String>(headers);
restTemplate.exchange("http://localhost:12001/api/profiles/bymsisdn/0747894146", HttpMethod.GET, entity, String.class);
Note that this is a tentative solution, adapt to your server.
curl uses / as the Accept header when you do not specify one. With RestTemplate if you do not specify the accept header , it attempts to find a suitable MIME type to your response type (in your case String). For String class depending on your configuration can match text/* MIME types which might not match what the target endpoint produces.
Try to explictly specify the content type that you need to handle using the exchange overloaded methods of RestTemplate
final String uri = http://localhost:8088/myapp/{data}";
RestTemplate restTemplate = new RestTemplate();
MyResponse result = restTemplate.getForObject(uri,MyResponse.class,valData);

SpringBoot RestController generic POST type

I'm experimenting with building microservices using Spring Boot.
I have a back-end API that receives ResponseEntity POST requests and processes it (saving to database etc). Where Data is an Object of a self-created class.
Now I have a top-level API (that handles authentication,..). The end-users will communicate with the back-end services through this top-level API. So this API basically just has to forward all the requests to the right back-end api's.
In this top API I don't want to need to include all my classes (e.g. the Data class in this case) and I would rather just send it as String json data or something. So I tried this:
#RequestMapping(method = RequestMethod.POST, value="/data")
ResponseEntity<String> createUnit(#RequestBody String data) {
URI uri = util.getServiceUrl("dataservice");
String url = uri.toString() + "/data";
ResponseEntity<String> result = restTemplate.postForEntity(url, data, String.class);
return new ResponseEntity<String>(result.getBody(), HttpStatus.OK);
}
But this results in an org.springframework.web.client.HttpClientErrorException: 415 Unsupported Media Type.
So my question is, is there a way to forward these requests to my back-end without the need to include all my Object classes in my API? I figured this should be able since this is the same as when a web-browser sends requests in json format without knowing what kind of Object the data actually is.
The back-end handling looks like this:
#RequestMapping(method = RequestMethod.POST, value="/data")
ResponseEntity<Data> saveData(#RequestBody Data data) {
//Some code that processes the data
return new ResponseEntity<Data>(dataProcessed, HttpStatus.OK);
}
When posting the String to the backend service you have to specify the Content-Type header so Spring knows which HttpMessageConverter to use to deserialize the Data object.
With RestTemplate you can specify the header like this:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<String>(data, headers);
restTemplate.postForEntity(url, entity, responseType);
Even though the question was already answered, I'll show another way I managed to solve the problem.
I used type Object instead of String:
#RequestMapping(method = RequestMethod.POST, value="/data")
ResponseEntity<Object> createUnit(#RequestBody Object data) {
URI uri = util.getServiceUrl("dataservice");
String url = uri.toString() + "/data";
ResponseEntity<Object> result = restTemplate.postForEntity(url, data, Object.class);
return new ResponseEntity<Object>(result.getBody(), HttpStatus.OK);
}

How to set accept media type in RestTemplate (Spring restful client)

I am getting xml response from the spring restful service by default.
Using RestTemplate in my spring restful client how can I configure the accept media type to JSON?
If the Rest Service is producing only XML, then I don't think you will be able to accept it as JSON. In that case what you need to do is add the MediaType as "application/json" in the Rest Service, along with the existing xml response.
For e.g in Spring Restful Service the annotation would be
#RequestMapping(value = "/myurl", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
If the Rest Service is producing Json and Xml, then in the rest client you need to do :
ResponseEntity<YourClass> apiResp = restTemplate.exchange(url, HttpMethod.GET, request, YourClass.class);
YourClass output=apiResp.getBody();

Categories