Как проанализировать тело ответа в Java, когда HTTP-запрос имеет статус возврата 401

Я использую RESTful JSON API, используя Spring RestTemplate и Jackson. В некоторых случаях мы можем получить ответ Status 401 (неавторизованный) с настраиваемым телом JSON, который определен производителем API, и выглядит следующим образом:

{
    "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;
    }
}

И пользовательский обработчик ошибок для этого:

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

Обработчик ошибок не работает с HttpMessageNotReadableException в блоке try:

"Не удалось прочитать JSON: не удается выполнить повторную попытку из-за аутентификации сервера в режиме потоковой передачи"

Вот как я отправляю запросы:

restTemplate.postForObject(url, pojoInstance, responseClass);

Если один и тот же запрос выполняется с простой старой клиентской программой для отдыха, например Postman, ожидается ожидаемый ответ JSON. Таким образом, я предполагаю, что проблема может быть с реализацией Spring ClientHttpResponse, так или иначе не позволяющей получить доступ к телу ответа в случае статуса 401.

Действительно ли можно проанализировать тело ответа?

Обновление

Из того, что я исследовал, класс RestTemplate использует ClientHttpResponse, который, в свою очередь, создает sun.net.www.protocol.http.HttpURLConnection, который предоставляет входной поток. Он существует там, где игнорируется входной поток и бросается IOException:

не может повторить попытку из-за аутентификации сервера, в потоковом режиме

Итак, реализация HttpURLConnection вызывает проблему.

Можно ли избежать этой проблемы? Возможно, нам следует использовать альтернативную реализацию, которая не игнорирует тело ответа в случае кода состояния ошибки? Можете ли вы порекомендовать какие-либо альтернативы?

Ответ 1

Попробуйте выполнить следующий подход, не требуя специального обработчика. Идея состоит в том, чтобы получить ответ как строку из 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);
            }
        }

Update: Использование другого factory также может помочь, так как есть ошибка по умолчанию, связанная с вашей проблемой (см. Комментарий ниже):

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