当HTTP请求具有返回状态401时,如何在Java中解析响应主体

我正在使用Spring的RestTemplate和Jackson来使用RESTful JSON API。 在某些情况下,我们可能会收到一个Status 401 (未授权)响应,其中包含由API制造商定义的自定义JSON主体,如下所示:

 { "code": 123, "message": "Reason for the error" } 

我们需要解析主体,并在业务逻辑中使用code属性。

这是我们需要解析的错误响应Java对象:

 public class CustomError { @JsonProperty private Integer code; @JsonProperty private String message; public Integer getCode() { return code; } public String getMessage() { return message; } } 

以及一个自定义error handling程序:

 public class CustomErrorHandler extends DefaultResponseErrorHandler { private RestTemplate restTemplate; private ObjectMapper objectMapper; private MappingJacksonHttpMessageConverter messageConverter; @Override public boolean hasError(ClientHttpResponse response) throws IOException { return super.hasError(response); } @Override public void handleError(final ClientHttpResponse response) throws IOException { try { CustomError error = (CustomError) messageConverter.read(CustomError.class, response); throw new CustomErrorIOException(error, error.getMessage()); } catch (Exception e) { // parsing failed, resort to default behavior super.handleError(response); } } } 

error handling程序因try块中的HttpMessageNotReadableException而失败:

“无法读取JSON:由于服务器身份validation无法在流模式下重试”

这就是我发送请求的方式:

 restTemplate.postForObject(url, pojoInstance, responseClass); 

如果使用普通的旧rest客户端程序(如Postman)执行相同的请求,则会收到预期的JSON响应。 所以,我假设问题可能是Spring的ClientHttpResponse实现以某种方式不允许访问响应体,如果是401状态。

是否确实可以解析响应主体?

更新

根据我的调查, RestTemplate类使用ClientHttpResponse ,后者又创建了一个提供输入流的sun.net.www.protocol.http.HttpURLConnection 。 它就在那里,忽略输入流并抛出IOException

在流模式下,由于服务器身份validation无法重试

因此, HttpURLConnection的实现导致了这个问题。

是否可以避免这个问题? 也许我们应该使用一个替代实现,在发生错误状态代码时不会忽略响应主体? 你能推荐任何替代品吗?

无需自定义处理程序即可尝试以下方法。 我们的想法是将响应作为HttpStatusCodeException中的字符串获取,然后您可以将其转换为您的对象。 对于转换,我使用了Jackson的ObjectMapper:

  try { restTemplate.postForObject(url, pojoInstance, responseClass); } catch (HttpStatusCodeException e) { if (e.getStatusCode() == HttpStatus.UNAUTHORIZED) { String responseString = e.getResponseBodyAsString(); ObjectMapper mapper = new ObjectMapper(); CustomError result = mapper.readValue(responseString, CustomError.class); } } 

更新:使用其他工厂也可能有所帮助,因为与您的问题相关的默认错误中存在错误(请参阅下面的评论):

 RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());