POSTing multipart requests with RestTemplate

Some days ago I had to write a little client application to call a RESTful web service; my job was to upload some data, defined in terms of JSON, along with a picture (PNG format, but that does not matter for this time). These tasks are usually carried out with POSTing form/multipart messages to the endpoint – my case was no different, the server was expecting just that. I thought it would be easy and fast to throw in RestTemplate from Spring, but as it turned out, this simple task took me around 12 hours, a complete Sunday and a lot of pain (finally, Sotirios Delimanolis pointed me in the right direction on StackOverflow.com). As it seems, there is little amount of code examples out there, so I thought I’d post one here.

So, as I’ve already mentioned, the main parameters in this post are: RestTemplate, form/multipart POST requests, JSON and binary (picture) data Let’s get started.

The first thing is message converters. For converting Java objects to JSON, we need a MappingJackson2HttpMessageConverter (take care, for this to work, you have to annotate your Java object with the corresponding Jackson annotations). For the binary part of the message (the image, in this case), we need to use either a ByteArrayHttpMessageConverter (for byte[] format), or a ResourceHttpMessageConverter (for ByteArrayResource).

Usually these simple message converters are enough for producing web service requests, but in this case, -as we need them to kind of co-operate – a FormHttpMessageConverter is needed. This one differs a bit from the other message converters, because it wraps other HttpMessageConverters in a field called partConverters. These part converters are responsible for converting different entities into one single request. In our case, this looks something like this:

--FormHttpMessageConverter
|
|-- MappingJackson2HttpMessageConverter
|-- ResourceHttpMessageConverter

This is how the message converters are constructed:

ArrayList<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>(
        Arrays.asList(new MappingJackson2HttpMessageConverter(), new ResourceHttpMessageConverter()));
RestTemplate restTemplate = new RestTemplate(converters);

Remember to register a message converter for the value returned by the web service. This can be done with the following call:

restTemplate.getMessageConverters().add(new WhateverTypeYouNeedHttpMessageConverter());

Fine. Now, let’s construct the request message itself.
First, we need a message header, that will correctly identify our request as form/multipart:

HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.MULTIPART_FORM_DATA);

Request header is ready, let’s take some care about message body. The message body will be contained in a MultiValueMap. This map will contain the two parts of the message, in two different HttpEntity objects. Both these entities will have their own sub-headers, so they will be identified as application/json and image/png. Let’s see how it’s done:

MultiValueMap<String, Object> multipartRequest = new LinkedMultiValueMap<>();

// creating an HttpEntity for the JSON part
HttpHeaders jsonHeader = new HttpHeaders();
jsonHeader.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<JsonObject> jsonHttpEntity = new HttpEntity<>(jsonObject, jsonHeader);

// creating an HttpEntity for the binary part
HttpHeaders pictureHeader = new HttpHeaders();
pictureHeader.setContentType(MediaType.IMAGE_PNG);
HttpEntity<ByteArrayResource> picturePart = new HttpEntity<>(pngPicture, pictureHeader);

// putting the two parts in one request
multipartRequest.add("myAwesomeJsonData", jsonPart);
multipartRequest.add("file", picturePart);

HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(multipartRequest, header);
ResultObject result = restTemplate.postForObject(UPLOAD_URL, requestEntity, ResultObject.class);

And basically that’s it: two simple HttpEntities, wrapped in a big, composite HttpEntity. When inspected, this request will look something like (catched using WireShark):

POST WEB_SERVICE_ENDPOINT HTTP/1.1
Accept: application/json, application/*+json
Content-Type: multipart/form-data;boundary=Rty3Jyemg94KrwQLR9AbfGx0cjuY6TAb
Content-Length: 34673
User-Agent: Dalvik/2.1.0 (Linux; U; Android 5.0.1; Android SDK built for x86 Build/LSX66B)
Host: WS_HOST
Connection: Keep-Alive
Accept-Encoding: gzip

–Rty3Jyemg94KrwQLR9AbfGx0cjuY6TAb
Content-Disposition: form-data; name=”myAwesomeJsonObject”
Content-Type: application/json

{“key”: “value”}
–Rty3Jyemg94KrwQLR9AbfGx0cjuY6TAb
Content-Disposition: form-data; name=”file”; filename=”THE_FILENAME_SET_IN_BYTE_ARRAY_RESOURCE”
Content-Type: image/png
Content-Length: 17321

.PNG
.
SOME_BINARY_DATA
–Rty3Jyemg94KrwQLR9AbfGx0cjuY6TAb-

As you can see, there is a top header (multipart/form-data) and one sub-header for both entities.
And that’s all folks!

Advertisements

Author: tamasgyorfi

Senior software engineer, certified enterprise architect and certified Scrum master. Feel free to connect on Twitter: @tamasgyorfi

2 thoughts on “POSTing multipart requests with RestTemplate”

  1. I tried using your code but it gives me following error
    org.springframework.web.client.RestClientException: Could not write request: no suitable HttpMessageConverter found for request type [org.springframework.util.LinkedMultiValueMap] and content type [multipart/form-data]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s