【问题标题】:How can I get data from a response after `RestTemplate` throws a `RestClientException` because it couldn't parse it在`RestTemplate`抛出`RestClientException`后如何从响应中获取数据,因为它无法解析它
【发布时间】:2019-08-11 00:23:38
【问题描述】:

当响应是错误的responseType 时,使用restTemplate.exchange(uri, method, entity, responseType) 进行REST 调用失败并返回RestClientException。例如,

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

由于这不是RestClientResponseException,我们似乎无法访问响应数据,例如状态代码和正文(即使是原始形式)。

有没有办法从原始(不可解析的)响应中获取(原始)数据? (用于记录)

【问题讨论】:

  • 嗨,您是否尝试过 getRawStatusCode()getResponseBodyAsString() 在 spring 文档中提到的 here
  • 这些是RestClientResponseException 的方法,但它们在RestClientException 中不可用。
  • 哦,请看,因为这不是RestClientResponseException。因此,您正在寻找来自上游服务器的原始响应(至少),尽管 restTemplate 由于 n 个原因而无法解析,但就像您提到的那样。

标签: spring exception response resttemplate


【解决方案1】:

尝试将响应解析为String。请参阅this answer - 类似的概念可以与exchange 方法一起使用。

编辑:如果异常并非总是发生并且您仍然希望能够轻松映射正确的响应,则可以覆盖相应的 MessageConverter(实际上是抛出异常)和之后做任何你想做的事情,因为转换器会给你一个原始的HttpInputMessage

假设您使用的是MappingJackson2HttpMessageConverter,它看起来应该是……像这样(虽然没有测试)

@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;
            }
        }
    };
}

【讨论】:

  • 但是我必须自己解析成所需类型的对象? Spring 是否提供该功能?
  • 当然,但是您要求原始响应。这不是你想要的吗?
  • 如果出现问题,原始(字符串)数据很好,因为(在我的情况下)它只是用于记录。但在快乐的流程中,我希望有类型化的响应正文。
  • 这意味着如果我想在特殊情况下获得该信息,我应该始终将响应读取为字符串,并尝试自己将其解析为预期的响应类型。这样,如果发生解析异常,则可以使用字符串信息。但这意味着我必须自己进行解析。
  • 这似乎是一种方法。 inputMessage 的正文有一个 InputStream,所以我必须缓存它,这样如果出现问题,我实际上可以第二次读取它。但是我可以在这里访问我需要的一切,而不必调用端点两次并且仍然使用现有的解析功能。
【解决方案2】:
 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); 

        }

【讨论】:

  • 这只是进行两次相同的调用。操作昂贵。
  • 因为万一出现故障需要再次调用。
  • 但是是什么原因呢?我认为该信息已被检索,因此它已经在客户端。记录除实际导致错误的响应之外的响应是很奇怪的。连续调用返回相同的信息并不总是正确的。
  • 由于我们没有实际的响应类型,我们有两个选项可以在收到 /above 后手动映射响应
  • 这是您的解决方案所允许的,但这不是您的解决方案必不可少的原因。我不想进行两次相同的网络调用。
【解决方案3】:

尝试在RestTemplate的messageConverters中添加StringHttpMessageConverter

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

RestTemplate restTemplate = new RestTemplate(factory);
restTemplate.setMessageConverters(
            List.of(stringHttpMessageConverter, jackson2HttpMessageConverter));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多