3

Using restTemplate.exchange(uri, method, entity, responseType) to make a REST call fails with a RestClientException when the response is of the wrong responseType. E.g.,

org.springframework.web.client.RestClientException: Error while extracting response for type [java.util.List<java.lang.Byte>] and content type [application/json;charset=UTF-8];
nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Numeric value (281759) out of range of Java byte

Since this is not a RestClientResponseException we don't seem to have access to the response data like status code and body (not even in their raw form).

Is there a way to get (raw) data from the original (unparsable) response? (for logging)

neXus
  • 2,005
  • 3
  • 29
  • 53
  • Hi, Did you tried `getRawStatusCode()` & `getResponseBodyAsString()` as mentioned [here](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestClientResponseException.html#getRawStatusCode--) in spring doc, – Vivek Mar 20 '19 at 13:38
  • Those are methods of `RestClientResponseException` but they are not available in `RestClientException`. – neXus Mar 20 '19 at 14:16
  • Ohhk, just read , _Since this is not a `RestClientResponseException`_ . So you're looking for raw response (atleast) from the upstream server though `restTemplate` failed to parse because of n reasons but one being like you mentioned. – Vivek Mar 20 '19 at 16:09

3 Answers3

0

Try parsing the response as String. See this answer - the similar concept can be used with the exchange method.

EDIT: If the exception does not occur always and you still want to be able to map the correct responses easily, you could override the corresponding MessageConverter (which is actually throwing the exception) and do anything you want afterwards, because the converter gives you a raw HttpInputMessage.

Assuming you are using MappingJackson2HttpMessageConverter it should look sth. like this (not tested though):

@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
    return new MappingJackson2HttpMessageConverter(objectMapper) {
        @Override
        public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
            try {
                return super.read(type, contextClass, inputMessage);
            } catch (HttpMessageNotReadableException e) {
                // LOG here...
                throw e;
            }
        }
    };
}
stuchl4n3k
  • 588
  • 5
  • 14
  • But then I have to do the parsing into an object of the desired type myself? Does Spring offer that functionality? – neXus Mar 20 '19 at 11:05
  • Of course, but you asked for the raw response. Isn't this what you wanted? – stuchl4n3k Mar 21 '19 at 12:28
  • The raw (String) data is fine if something goes wrong, since (in my case) it would be just for logging. But in the happy flow I would like to have the typed response body. – neXus Mar 21 '19 at 14:28
  • This means that if I want to have that information in the exceptional cases, I should always read the response as a String, and try to parse it myself into the expected response type. That way the String information is available if a parsing Exception occurs. But this means I have to do the parsing myself. – neXus Mar 21 '19 at 14:29
  • This seems like a way to go. The body of the `inputMessage` there is an `InputStream` so I have to cache it so I can actually read it a second time if something goes wrong. But I have access to everything I need here, without having to call the endpoint twice and still using the existing parsing functionality. – neXus Mar 27 '19 at 08:46
0

Try to add StringHttpMessageConverter in RestTemplate's messageConverters

StringHttpMessageConverter stringHttpMessageConverter
            = new StringHttpMessageConverter(StandardCharsets.UTF_8);
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter
            = new MappingJackson2HttpMessageConverter(objectMapper);

RestTemplate restTemplate = new RestTemplate(factory);
restTemplate.setMessageConverters(
            List.of(stringHttpMessageConverter, jackson2HttpMessageConverter));
MiraGe
  • 311
  • 4
  • 8
-1
 final RestTemplate restTemplate = new RestTemplate();
        try { 
            restTemplate.exchange(uri, method, entity, responseType);
        } catch (RestClientException e) {
            //for logging exact message
            restTemplate.getForObject("https://httpbin.org/ip", String.class); 

        }
  • This is just making the same call twice. Expensive operation. – neXus Mar 22 '19 at 08:31
  • Since in case of failure you need to call it again . – manoj mallick Mar 22 '19 at 08:33
  • But what is the reason? I would think that the information is already retrieved so it is already on the client side. It's weird to log a response other than the one that actually caused the error. It's not always true that successive calls return the same info. – neXus Mar 22 '19 at 08:52
  • Since we don't the actual response type , we have two options to manually mapping the response after receiving /above – manoj mallick Mar 22 '19 at 09:06
  • That is what your solution allows, but it's not the reason why your solution is necessary. I don't want to be making the same network call twice. – neXus Mar 22 '19 at 09:50