Reputation: 14675
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?
Upvotes: 13
Views: 27259
Reputation: 29
I too have found this a disturbing change in the Spring library. You used to be able to throw a ResponseStatusException and pass it the HttpStatus code and a custom message and then catch the HttpClientErrorException and simply print getMessage() to see the custom error. This no longer works. In order for me to print the custom error, I had to capture the ResponseBody as a String, getResponseBodyAsString on the HttpClientErrorException. Then I needed to parse this as a String doing some pretty hokey substring manipulation. Doing this strips out the information from the ResponseBody and gives the message sent by my server. The code to do this follows:
String message = hce.getResponseBodyAsString();
int start, end;
start = message.lastIndexOf(":", message.lastIndexOf(",")-1) + 2;
end = message.lastIndexOf(",") -1;
message = message.substring(start, end);
System.out.println(message);
When I test this using ARC or Postman, they can display the correct message after I add server.error.include-message=always to my application.properties file on the server. I am not sure what method they are using to extract the message but that would be nice to know.
Upvotes: 1
Reputation: 3777
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);
Upvotes: 8
Reputation: 361
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.
Upvotes: 2
Reputation: 8414
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.
Upvotes: 14