Creating custom HttpMessageConverters for Spring’s RestTemplate

Yesterday, I struggled with one of ours REST API which is old and oddly designed. The API always returns, 200 HTTP status code in the response header, no matter the request is successful or not. Response body has a field called as “code” along with some other fields which represents the actual status code. In situations when the ‘actual’ response code is non-200 in the response body, HTTP response header code is still 200 and RestTemplate thinks that this is a genuine response and tries to deserialize the response body and fails! This made me write my own HttpMessageConverter that I could pass on to RestTemplate to deserialize the response object.

I looked at MappingJackson2HttpMessageConverter source and pretty much kept the source except for the readInternal() method. Rest of the methods in my converters are exactly like MappingJackson2HttpMessageConverter, hence I have taken out the duplicate methods below for brevity.

public class CustomHttpmsgConverter extends  AbstractHttpMessageConverter<Object> {

    public static final Charset DEFAULT_CHARSET
    = Charset.forName("UTF-8");
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected Object readInternal(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {

      InputStream istream = inputMessage.getBody();
      String responseString = IOUtils.toString(istream);
      JavaType javaType = getJavaType(clazz);
      try {

        return this.objectMapper.readValue(responseString, javaType);

      } catch (Exception ex) {

        throw new HttpMessageNotReadableException(responseString);

      }

    }

    Notice the use of string in object mapper for deserialization. Original implementation in MappingJackson2HttpMessageConverter uses input stream to deserialize, but I first use it to convert it to string since I need to preserve the response body. HttpInputMessage stream can only be used once because it is closed internally after it is fetched for the first time. Therefore, I can only use string to deserialize in my converter. This is important to note.

    In my @Test I just catch the HttpMessageNotReadableException which has the reponseString if the message is not readable

    @Test
    public void test() {

      String url = "htt://my.application.com";
      RestTemplate template = new RestTemplate();
      List<HttpMessageConverter> converters = new ArrayList<HttpMessageConverter>();
      converters.add(new MDLmsgConverter());
      template.setMessageConverters(converters);
      MultiValueMap header = new LinkedMultiValueMap();
      header.add("Content-Type", "application/x-www-form-urlencoded");
      HttpEntity requestEntity = new HttpEntity(null, header);
      try {
      ResponseEntity<Data> response = template.exchange(url, HttpMethod.POST,
      requestEntity, Data.class);
      Assert.isTrue(response.getBody().equals(expectedDataObject));
      } catch (HttpMessageNotReadableException e) {
      Assert.isTrue(e.getMessage().equals(
      "this is what the response body was"));
      }

    }

    Advertisements