48

The methods of RestTemplate such as postForEntity() throw RestClientException. I would like to extract the HTTP status code and response body from that exception object in the catch block. How can I do that?

Tristan
  • 8,733
  • 7
  • 48
  • 96
Vathanak
  • 481
  • 1
  • 4
  • 4

4 Answers4

75

Instead of catching RestClientException, catch the special HttpClientErrorException.

Here's an example:

try {
    Link dataCenterLink = serviceInstance.getLink("dataCenter");
    String dataCenterUrl = dataCenterLink.getHref();
    DataCenterResource dataCenter =
        restTemplate.getForObject(dataCenterUrl, DataCenterResource.class);
    serviceInstance.setDataCenter(dataCenter);
} catch (HttpClientErrorException e) {
    HttpStatus status = e.getStatusCode();
    if (status != HttpStatus.NOT_FOUND) { throw e; }
}

HttpClientErrorException provides getStatusCode and getResponseBodyAsByteArray to get the status code and body, respectively.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • 1
    more generic one is `HttpStatusCodeException` inherited by `HttpClientErrorException` and `HttpServerErrorException`. – Dariush Jafari Feb 15 '18 at 04:30
  • 5
    If you catch `HttpStatusCodeException`, you have access to the method `getResponseBodyAsString()`. – Nikhil Sahu Nov 16 '18 at 09:53
  • 4
    What am I missing with this answer? If you are calling a method that return a RestClientException you cannot just magically cast it to HttpClientErrorException right? – IcedDante Jun 14 '19 at 16:23
  • this answer is dangerous as `HttpClientErrorException` only catches 4xx codes but does not handle 5xx.. `RestClientResponseException` should be used instead, or you can add `HttpServerErrorException` to handle 5xx codes. – johnmweisz Feb 02 '23 at 18:07
9

Catch RestClientResponseException instead. It's more generic.

From the docs: Common base class for exceptions that contain actual HTTP response data.

Emmanuel Osimosu
  • 5,625
  • 2
  • 38
  • 39
  • Catching that exception works, however, I preferred the HttpClientErrorException because you can get both the response body as well as the full HttpStatus object / enum with the corresponding message. Example: you can get the "404" code along with the "Not found" description when using the other one. – atom88 Nov 05 '20 at 21:07
  • this is the correct answer as it handles more than just 4xx codes. – johnmweisz Feb 02 '23 at 18:15
1

In some cases, HttpClientErrorException is not thrown. For example the following method restTemplate.exchange call:

ResponseEntity<Employee[]> employees =  restTemplate.exchange(url, HttpMethod.GET, entity, Employee[].class);

Gets the http body and marshalls it to an Entity. If remote resource returns a rare error, internal marshall does not work and just a RestClientException is thrown.

restTemplate.setErrorHandler

In this case or if you want to handle any error in restTemplate operations, you could use setErrorHandler. This method receives a basic ResponseErrorHandler with helpful methods.

This method hasError allowed me to get the remote http body text and helped me to detect the error of the invocation or in the remote http remote resource:

restTemplate.setErrorHandler(new ResponseErrorHandler() {

  @Override
  public boolean hasError(ClientHttpResponse arg0) throws IOException {

    System.out.println("StatusCode from remote http resource:"+arg0.getStatusCode());
    System.out.println("RawStatusCode from remote http resource:"+arg0.getRawStatusCode());
    System.out.println("StatusText from remote http resource:"+arg0.getStatusText());

    String body = new BufferedReader(new InputStreamReader(arg0.getBody()))
          .lines().collect(Collectors.joining("\n"));

    System.out.println("Error body from remote http resource:"+body);
    return false;
  }

  @Override
  public void handleError(ClientHttpResponse arg0) throws IOException {
    // do something
  }
});

Also, you can manually evaluate the body or status and return true or false in order to flag as error or not.

Ruthresh
  • 23
  • 1
  • 6
JRichardsz
  • 14,356
  • 6
  • 59
  • 94
-1
private void sendActivity(StatsActivity statsActivity) throws InterruptedException 
{
    LibraryConnectorXapiEditView libraryConnectorXapiEditView = (LibraryConnectorXapiEditView) workerBundle.getConnector();
    
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
    Statement statement = libraryConnectorConverter.convertActivityToStatement(statsActivity, workerBundle);
    HttpEntity<Statement> request = new HttpEntity<>(statement, headers);
    
    try
    {
       String lrsEndPoint = libraryConnectorXapiEditView.getLrsEndPoint() + "/statements";
       ResponseEntity<String> response = restTemplate.exchange(lrsEndPoint, HttpMethod.POST, request, String.class);
       ocnCompletionEventDao.save(this.convertToOcnCompletionEvent(statsActivity, response.getBody(), response.getStatusCodeValue()));
    }
    catch (HttpClientErrorException ex)
    {
      ocnCompletionEventDao.save(this.convertToOcnCompletionEvent(statsActivity, ex.getResponseBodyAsString(), ex.getStatusCode().value()));
      checkResponse(ex, libraryConnectorXapiEditView);  
      if(failedAttempts<3) 
      { 
          sendActivity(statsActivity);
          failedAttempts++;
      }
    }   
}

private void checkResponse(HttpClientErrorException ex, LibraryConnectorXapiEditView libraryConnectorXapiEditView) throws InterruptedException 
{
    int statusCode = ex.getStatusCode().value();
    int retryAfterSeconds = retryAfter(ex.getResponseHeaders());
    
    switch (statusCode)
    {
    case 401: 
        headers = xApiAuthorizationUtils.getHeaders(libraryConnectorXapiEditView);
    case 429:
        if(retryAfterSeconds!=0)
            Thread.sleep(retryAfterSeconds);
    case 422: 
        failedAttempts=3;
    }
 }
  • 1
    Please don't post only code as answer, but also provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes. – Pouria Hemi Nov 13 '20 at 22:57