Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to parse the response body in Java, when the HTTP request has return status 401

I am consuming a RESTful JSON API using Spring's RestTemplate and Jackson. In some cases we may receive a Status 401 (Unauthorized) response with a custom JSON body, that is defined by the API manufacturer, and looks like this:

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

We need to parse the body, and use the code property in our business logic.

This is the error response Java object we need to parse to:

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

And a custom error handler to do this:

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

The error handler fails with an HttpMessageNotReadableException in the try block:

"Could not read JSON: cannot retry due to server authentication, in streaming mode"

This is how I am sending requests:

restTemplate.postForObject(url, pojoInstance, responseClass); 

If the same request is executed with a plain old rest client program, like Postman, the expected JSON response is received. So, I assume the problem could be with the Spring's ClientHttpResponse implementation somehow not allowing access to the response body, in case of the 401 status.

Is it indeed possible to parse the response body?

Update

From what I investigated, the RestTemplate class uses ClientHttpResponse which in turn creates an sun.net.www.protocol.http.HttpURLConnection that provides the input stream. It is there, where the input stream is being neglected and an IOException is thrown:

cannot retry due to server authentication, in streaming mode

So, the HttpURLConnection's implementation is causing the issue.

Will it be possible to avoid this problem? Perhaps we should use an alternative implementation that does not ignore the response body in case of an error status code? Can you recommend any alternatives?

like image 671
Ivaylo Slavov Avatar asked Oct 31 '14 13:10

Ivaylo Slavov


People also ask

How do you get response body from HTTP response?

To get the response body as a string we can use the EntityUtils. toString() method. This method read the content of an HttpEntity object content and return it as a string. The content will be converted using the character set from the entity object.

How do you parse a Resttemplate response?

You can use the annotation @JsonRootName to specify the root element in your response. So try this: @JsonIgnoreProperties(ignoreUnknown = true) @JsonRootName(value ="result") public class User { public User(){ } private boolean admin; .... }

How to get httpresponse status and response as a string?

You can avoid the BasicResponseHandler, but use the HttpResponse itself to get both status and response as a String. HttpResponse response = httpClient.execute (get); // Getting the status code. int statusCode = response.getStatusLine ().getStatusCode (); // Getting the response body.

What is httpclient and httpresponse?

Mainly, this API consists of three core classes: HttpClient, HttpRequest, and HttpResponse. HttpResponse describes the result of an HttpRequest call. HttpResponse isn't created directly and is made available when the body has been fully received. To read a response body as a String, we'll first need to create simple client and request objects:

How to handle specific status codes and get response bodies?

An ExchangeFilterFunction is another way to handle specific status codes and get response bodies. Unlike onStatus, the exchange filter is flexible and applies to filter functionality based on any boolean expression. We can benefit from the flexibility of an ExchangeFilterFunction to cover the same categories as the onStatus function.

How do I get the body of a string using HTTP?

The simplest way to perform an HTTP Get request is to use create method: The simplest way to perform an HTTP Get request is to call the get and retrieve methods. Then, we'll use the bodyToMono method with the String.class type to extract the body as a single String instance:


1 Answers

Try the following approach without needing a custom handler. The idea is to get the response as a string from the HttpStatusCodeException, and then you can convert it to your object. For the conversion I used the Jackson's 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: Usage of a different factory may also help since there is a bug in the default one related to your issue (see comment below):

RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory()); 
like image 176
Marios Avatar answered Oct 02 '22 15:10

Marios