When working with APIs, there may be a need to send a request with JSON data and receive a response in multipart/form-data format containing a file. In this article, we will discuss how to configure RestTemplate to handle such data.
Create a class for serializing JSON to send
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class JsonRequest {
String field1;
int field2;
}
Configure RestTemplate, prepare, and send a POST request
public Optional<Resource> postSomeReq(JsonRequest rq) {
String url = "http://somehost/services/retrieve-multipart";
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(List.of(MediaType.MULTIPART_FORM_DATA));
RestTemplate restClient = new RestTemplate();
ResponseEntity<byte[]> response = restClient.postForEntity(url, getRequestData(rq, headers), byte[].class);
return getResource(response.getBody());
} catch (RestClientException | MessagingException | IOException e) {
return Optional.empty();
}
}
private HttpEntity<String> getRequestData(Object request, HttpHeaders headers) {
String requestBody;
try {
requestBody = new ObjectMapper().writeValueAsString(request);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
return new HttpEntity<>(removeShielding(requestBody), headers);
}
private Optional<Resource> getResource(byte[] responseBody) throws MessagingException, IOException {
ByteArrayDataSource dataSource = new ByteArrayDataSource(responseBody, "multipart/form-data");
MimeMultipart multipart = new MimeMultipart(dataSource);
int count = multipart.getCount();
for (int i = 0; i < count; i++) {
BodyPart bodyPart = multipart.getBodyPart(i);
String[] contentType = bodyPart.getHeader("Content-Type");
if ("application/octet-stream".equals(contentType[0])) {
SharedByteArrayInputStream inputStream = (SharedByteArrayInputStream) bodyPart.getContent();
final String filename = bodyPart.getFileName();
Resource resource = new ByteArrayResource(inputStream.readAllBytes()) {
@Override
public String getFilename() {
return filename;
}
};
return Optional.of(resource);
}
}
return Optional.empty();
}
Alternative Approach
You can also use a third-party converter MultipartMessageConverter
(GitHub Repository). It provides a simple implementation of HttpMessageConverter
that can read multipart/form-data
and prepare a DTO model, even if a part contains either application/json
or application/octet-stream
.
public Optional<Resource> postSomeReq(JsonRequest rq) {
String url = "http://somehost/services/retrieve-multipart";
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(List.of(MediaType.MULTIPART_FORM_DATA));
RestTemplate restClient = new RestTemplate();
restClient.getMessageConverters().add(new MultiPartMessageConverter(objectMapper));
ResponseEntity<ResultModel> responseEntity =
restClient.postForEntity(url, getRequestData(rq, headers), ResultModel.class);
final ResultModel resultModel = responseEntity.getBody();
return Optional.of(resultModel.fileModel.resource);
} catch (RestClientException | MessagingException | IOException e) {
return Optional.empty();
}
}
Conclusion
We have reviewed how to use RestTemplate to send a JSON request and receive a response in multipart/form-data
format. The approach presented allows for effective handling of multipart responses, which is useful in various scenarios, such as downloading files from a server. An alternative approach using a third-party converter was also presented.
You can now use these methods in your applications to work with multipart data, enhancing the functionality and flexibility of your code.