Spring RestTemplate exchange POST HttpClientException with any non 200 OK response

11,212

Solution 1

There's nothing wrong here. This exception will be thrown if you receive erroneous status code.

You just need to wrap your client side code in a try-catch and catch the exception and then do whatever you want to do with it.

try {
    ResponseEntity<String> response = restTemplate.exchange(fruitBasketUrl, HttpMethod.POST, fruit, String.class);
} catch (HttpStatusCodeException e) {
    e.getMessage();
}

Solution 2

The default behavior of RestTemplate on encountering error response codes is throwing an exception. In case of 4xx it's HttpClientErrorException and in case of 5xx: HttpServerErrorException (both extending HttpStatusCodeException). Spring achieves that by using ResponseErrorHandler (and it's default imlpementation - DefaultResponseErrorHandler)

One way of handling this would be to catch them:

try {
    ResponseEntity<String> response = restTemplate.exchange(fruitBasketUrl, HttpMethod.POST, fruit, String.class);
} catch(HttpClientErrorException e) {
    //handle 4xx errors
} catch(HttpServerErrorException e) {
    //handle 5xx errors
}

If you need to customize this behaviour (some rest API's use those codes when sending legitimate responses to some requests which you then may want to process as you do with 2xx response), you can create your own implementation of ResponseErrorHandler by implementing it or extending DefaultResponseHandler and then registering your handler with the RestTemplate during it's initialization:

public class MyResponseErrorHandler extends DefaultResponseErrorHandler {

    @Override
    public boolean hasError(ClientHttpResponse response) throws IOException {
        // check if response code is an error in here or just use default implementation
    }

    @Override
    public void handleError(ClientHttpResponse response) throws IOException {
        // handle different response codes
        // (default spring behaviour is throwing an exception)
    }
}

And registering:

RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new MyResponseErrorHandler());
// now RestTemplate's behaviour for error status codes is customized
Share:
11,212

Related videos on Youtube

iranicus
Author by

iranicus

Updated on October 13, 2022

Comments

  • iranicus
    iranicus over 1 year

    So the current issue I am having is I am getting a

    Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.HttpClientErrorException: 404 null] with root cause org.springframework.web.client.HttpClientErrorException 404 null

    in my client code when my server code responds with anything other than a OK 200 response. So in this case I was intentionally returning a 404 response in my server code having done so with a header and body, and without a header and body but I still get the same exception with the HTTP status code I respond with in the server code and null which in this case I assume to be the body of the response. Originally in my server code I always returned a ResponseEntity<>("Sent", HttpStatus.OK) but since in my server code I make a HTTP request elsewhere if this responds with anything other than a 200 OK then my client code wouldn't know about it therefore instead I am returning the actual response returned from the HTTP request in my server code back to my client code which is when I ran into this issue.

    Client Code

    public String callFruitBasket(Fruit fruitRequest) {
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
    
        HttpEntity<Fruit> fruit = new HttpEntity<>(fruitRequest, requestHeaders);
        System.out.println("Fruit headers: " + fruit.getHeaders());
    
        ResponseEntity<String> response = restTemplate.exchange(fruitBasketUrl, HttpMethod.POST, fruit, String.class);
        System.out.println("Full response: " + response);
    
        return response.getBody();
    }
    

    Server Code

    @PostMapping("/fruitBasket/send")
    public ResponseEntity<String> sendFruitBasket(@RequestBody Fruit fruit) {
       // works fine, old response
       // return new ResponseEntity<>("Sent", HttpStatus.OK);
    
       return new ResponseEntity<>("Baaad Request", HttpStatus.BAD_REQUEST);
    }
    

    So at the moment in my server code I didn't add any headers to my response but I have already tried this with adding a Content-Type however I found I still got the same exception in my client code so I am 100% sure that the issue lies somewhere in my client code. Originally when I passed the 200 OK response the printout of the full response was fine in the client code with showing:

    Full Response: <200 OK,Sent,{Content-Type=[text/plain;charset=UTF-8], Content-Length=[4], Date=[Sat, 19 May 2018 09:10:21 GMT]}>
    

    What I would expect for any of the other Http Status code like 400 and 404 would be the same but with the 400 or 404 in the response instead of the 200 OK. I've tried playing around with the headers in both client and server code as I have read in various posts on here that this is commonly the cause when getting this type of exception which leads me to believe there could be something fundamental missing in my client code or that this is expected behavior for exchange() and that I am misunderstanding it.