Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring RestTemplate Parse Custom Error Response

Given a REST service call

http://acme.com/app/widget/123

returns:

<widget>
  <id>123</id>
  <name>Foo</name>
  <manufacturer>Acme</manufacturer>
</widget>

This client code works:

RestTemplate restTemplate = new RestTemplate();
XStreamMarshaller xStreamMarshaller = new XStreamMarshaller();
xStreamMarshaller.getXStream().processAnnotations(
    new Class[] { 
        Widget.class,
        ErrorMessage.class
    });

HttpMessageConverter<?> marshallingConverter = new MarshallingHttpMessageConverter(
    xStreamMarshaller, xStreamMarshaller);

List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
    converters.add(marshallingConverter);

restTemplate.setMessageConverters(converters);

Widget w = restTemplate.getForObject(
    "http://acme.com/app/widget/{id}", Widget.class, 123L);

However, calling http://acme.com/app/widget/456 returns:

<error>
    <message>Widget 456 does not exist</message>
    <timestamp>Wed, 12 Mar 2014 10:34:37 GMT</timestamp>
</error>

but this client code throws an Exception:

Widget w = restTemplate.getForObject(
    "http://acme.com/app/widget/{id}", Widget.class, 456L);

org.springframework.web.client.HttpClientErrorException: 404 Not Found

I tried:

try {
    Widget w = restTemplate.getForObject(
       "http://acme.com/app/widget/{id}", Widget.class, 456L);
}
catch (HttpClientErrorException e) {
    ErrorMessage errorMessage = restTemplate.getForObject(
       "http://acme.com/app/widget/{id}", ErrorMessage.class, 456L);

   // etc...
}

The second invocation just threw another HttpClientErrorException, plus it does not feel right calling the service twice.

Is there a way to call the service once and parse the response into a Widget on success and an ErrorMessage when not found?

like image 568
Paul Croarkin Avatar asked Mar 12 '14 10:03

Paul Croarkin


3 Answers

You can also create an object from Error response body if you like:

ObjectMapper om = new ObjectMapper();
String errorResponse = ex.getResponseBodyAsString();

MyClass obj = om.readValue(errorResponse, MyClass.class);
like image 53
zygimantus Avatar answered Oct 10 '22 15:10

zygimantus


Following from my comment, I checked the HttpClientErrorException JavaDoc and it does support both setting/getting the statusText as well as the responseBody. However they are optional and RestTemplate may not populate them - you'll need to try something like:

try {
    Widget w = restTemplate.getForObject(
       "http://acme.com/app/widget/{id}", Widget.class, 456L);
}
catch (HttpClientErrorException e) {
    String responseBody = e.getResponseBodyAsString();
    String statusText = e.getStatusText();
    // log or process either of these...
    // you'll probably have to unmarshall the XML manually (only 2 fields so easy)
}

If they are both empty/null then you may have to extend the RestTemplate class involved and populate those fields yourself and/or raise a Jira issue on the Spring site.

like image 39
nickdos Avatar answered Oct 10 '22 15:10

nickdos


As you already catch the HttpClientErrorException object, it should allows you to easily extract useful information about the error and pass that to the caller.

For example:

try{
    <call to rest endpoint using RestTemplate>
} catch(HttpClientErrorException e){
    HttpStatus statusCode = e.getStatusCode();
    String body = e.getResponseBodyAsString();
}

IMO if one needs to further de-serialize the error message body into some relevant object, that can be handled somewhere outside of the catch statement scope.

like image 22
vu le Avatar answered Oct 10 '22 15:10

vu le