Spring RestTemplate解析自定义错误响应

时间:2014-03-12 10:55:34

标签: rest spring-mvc

给出REST服务调用

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

返回:

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

此客户端代码有效:

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);

但是,调用http://acme.com/app/widget/456会返回:

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

但是此客户端代码抛出异常:

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

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

我试过了:

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...
}

第二次调用只是抛出了另一个HttpClientErrorException,而且它感觉没有两次调用该服务。

有没有办法调用服务一次,并在成功时将响应解析为Widget,而在找不到时则解析ErrorMessage?

4 个答案:

答案 0 :(得分:9)

根据我的评论,我检查了HttpClientErrorException JavaDoc,它同时支持设置/获取statusText以及responseBody。但是它们是可选的,RestTemplate可能不会填充它们 - 你需要尝试类似的东西:

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)
}

如果它们都是空/ null,那么您可能必须扩展所涉及的RestTemplate类并自己填充这些字段和/或在Spring网站上引发Jira问题。

答案 1 :(得分:5)

如果您愿意,也可以从错误响应正文中创建对象:

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

MyClass obj = om.readValue(errorResponse, MyClass.class);

答案 2 :(得分:2)

由于您已经捕获了HttpClientErrorException对象,它应该允许您轻松提取有关错误的有用信息并将其传递给调用者。

例如:

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

IMO如果需要进一步将错误消息body反序列化为某个相关对象,可以在catch语句范围之外的某处处理。

答案 3 :(得分:-1)

我也在Spring库中发现了一个令人不安的更改。您曾经能够抛出ResponseStatusException并将其传递给HttpStatus代码和自定义消息,然后捕获HttpClientErrorException并只需打印getMessage()即可查看自定义错误。这不再起作用。为了让我打印自定义错误,我必须将ResponseBody捕获为HttpClientErrorException上的字符串getResponseBodyAsString。然后,我需要将其解析为一个字符串,进行一些漂亮的hokey子字符串操作。这样做会从ResponseBody中剥离信息,并给出我的服务器发送的消息。执行此操作的代码如下:

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);

当我使用ARC或Postman进行测试时,在我将server.error.include-message = always添加到服务器上的application.properties文件之后,它们可以显示正确的消息。我不确定他们使用哪种方法来提取消息,但是很高兴知道。