Cómo analizar el cuerpo de la respuesta en Java, cuando la solicitud HTTP tiene un estado de retorno 401

Estoy consumiendo una RESTful JSON API usando Spring’s RestTemplate y Jackson. En algunos casos, es posible que recibamos una respuesta del Status 401 (no autorizado) con un cuerpo JSON personalizado, definido por el fabricante de la API, y se parece a esto:

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

Necesitamos analizar el cuerpo y usar la propiedad del code en nuestra lógica de negocios.

Este es el objeto Java de respuesta de error que necesitamos analizar para:

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

Y un controlador de errores personalizado para hacer esto:

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

El controlador de errores falla con una HttpMessageNotReadableException en el bloque try:

“No se pudo leer JSON: no se puede reintentar debido a la autenticación del servidor, en modo de transmisión”

Así es como estoy enviando solicitudes:

 restTemplate.postForObject(url, pojoInstance, responseClass); 

Si se ejecuta la misma solicitud con un progtwig de cliente de descanso anterior, como Postman, se recibe la respuesta JSON esperada. Entonces, asumo que el problema podría estar en la implementación ClientHttpResponse de Spring que de alguna manera no permite el acceso al cuerpo de respuesta, en caso del estado 401.

¿Es realmente posible analizar el cuerpo de respuesta?

Actualizar

Por lo que he investigado, la clase RestTemplate utiliza ClientHttpResponse que a su vez crea un sun.net.www.protocol.http.HttpURLConnection que proporciona el flujo de entrada. Es allí, donde se descuida el flujo de entrada y se lanza una IOException :

no se puede volver a intentar debido a la autenticación del servidor, en modo de transmisión

Por lo tanto, la implementación de HttpURLConnection está causando el problema.

¿Será posible evitar este problema? ¿Quizás deberíamos usar una implementación alternativa que no ignore el cuerpo de la respuesta en caso de un código de estado de error? ¿Puedes recomendar alguna alternativa?

Pruebe el siguiente enfoque sin necesidad de un controlador personalizado. La idea es obtener la respuesta como una cadena de la excepción HttpStatusCodeException, y luego puede convertirla en su objeto. Para la conversión utilicé el ObjectMapper de Jackson:

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

Actualización: el uso de una fábrica diferente también puede ayudar, ya que hay un error en la predeterminada relacionado con su problema (vea el comentario a continuación):

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