【问题标题】:Spring RestTemplate Parse Custom Error ResponseSpring RestTemplate 解析自定义错误响应
【发布时间】:2014-04-16 10:55:10
【问题描述】:

给定一个 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?

【问题讨论】:

  • 我不确定您为什么担心从 XML 响应中获取确切的错误消息,因为它实际上与 HTTP 404 相同 - 未找到。只需捕获 404 并抛出新异常或生成您自己的消息等。
  • 如果此自定义消息被成功解析,我知道小部件未找到与服务已关闭或正在为服务调用错误的 URL。
  • 很公平,虽然“服务中断”应该是不同的代码 (500)。此时我将开始阅读 RestTemplate 的源代码...,您确定 HTTP 正文没有存储在 HttpClientErrorException 的某处吗?
  • 您可以访问下面的线程。它有完整的工作代码和描述:stackoverflow.com/a/51805956/3073945

标签: rest spring-mvc


【解决方案1】:

我在 Spring 库中也发现这是一个令人不安的变化。您曾经能够抛出 ResponseStatusException 并将 HttpStatus 代码和自定义消息传递给它,然后捕获 HttpClientErrorException 并简单地打印 getMessage() 以查看自定义错误。这不再有效。为了打印自定义错误,我必须在 HttpClientErrorException 上将 ResponseBody 捕获为字符串,getResponseBodyAsString。然后我需要把它解析为一个字符串,做一些非常狡猾的子字符串操作。这样做会从 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 文件后,它们可以显示正确的消息。我不确定他们使用什么方法来提取消息,但很高兴知道。

【讨论】:

    【解决方案2】:

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

    ObjectMapper om = new ObjectMapper();
    String errorResponse = ex.getResponseBodyAsString();
    
    MyClass obj = om.readValue(errorResponse, MyClass.class);
    

    【讨论】:

    • Jackson ObjectMapper 不是关于 JSON,而不是 XML 吗?问题是关于 XML,而不是 JSON。
    【解决方案3】:

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

    例如:

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

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

    【讨论】:

      【解决方案4】:

      根据我的评论,我检查了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 问题。

      【讨论】:

      • 这听起来很有希望,但没有抛出 HttpClientErrorException。相反,我得到 org.springframework.beans.TypeMismatchException: Failed to convert value of type 'com.acme.ErrorMessage' 到所需类型 'com.acme.Widget' ,它没有响应正文。我将看看扩展 RestTemplate。感谢您的建议!
      • 您最初的问题是“但是这个客户端代码抛出了一个异常:” - 一个 HttpClientErrorException。有什么改变吗?我建议当抛出此异常时,您应在其中处理它 - 也许我的代码具有误导性。
      • @PaulCroarkin 你有没有找到反序列化错误正文的解决方案?
      • 我也有同样的问题。在 API 服务中,我返回一个 ApiError 对象,其中包含错误代码(例如:423)和错误消息(例如:访问被拒绝)作为字段。当从浏览器执行时,它会正确返回 JSON 消息作为响应,但是当我使用 restTemplate.postForObject 调用它时,它会抛出带有正确错误代码的 HttpClientErrorException,而不是错误消息。
      猜你喜欢
      • 1970-01-01
      • 2018-01-29
      • 2014-12-01
      • 1970-01-01
      • 2019-04-19
      • 2015-09-04
      • 2016-02-02
      • 1970-01-01
      • 2021-02-24
      相关资源
      最近更新 更多