HttpClientErrorException 400 null using RestTempla

2019-08-18 00:14发布

问题:

I have two microservices. The first one receives a call from the Frontend and then it calls to the second uService to receive some data. The last is returning an error response (Bad Request, this is ok - it is a use-case). However, I am losing the body (message) returned from the second microservice, as the first is throwing a HttpClientErrorException 400 null in the call

This is my code:

ResponseEntity<MyEntity> entityResponse = restTemplate.getForEntity(url, MyEntity.class, id);

I am not able to do entityResponse.getStatusCode() as an exception is thrown.

Handled it in the ControllerAdvice, my exception message is "400 null" even I return a custom message from the service.

So, I would like to get the response message sent in the called uservice to manage it.

Thanks in advance.

回答1:

The answers here that explain how to catch the exception and access the body are correct. However, you may use a different approach. You can use a 3-d party library that sends Http request and handles the response. One of the well-known products would be Apache commons HTTPClient: HttpClient javadoc, HttpClient Maven artifact. There is by far less known but much simpler HTTPClient (part of an open source MgntUtils library written by me): MgntUtils HttpClient javadoc, MgntUtils maven artifact, MgntUtils Github. Using either of those libraries you can send your REST request and receive response independently from Spring as part of your business logic



回答2:

What I'm doing in my project is the following.

MicroService_2 calls MicroService_1.

MicroService_1

MicroService_1 returns for example a HTTP 404 exception if the entity isn't found.

@RestController
@RequestMapping(value = "/api/v1/")
public class Service1Controller {


    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public @ResponseBody MyEntity getMyEntity(@PathVariable String id) throws NotFoundException {
        MyEntity result = ...
        if(result == null) {
            throw new NotFoundException("MyEntity [id: "+id+"] not found");
        }

        return result;
    }


    @ControllerAdvice
    public class RestEndpointExceptionHandler extends RestExceptionHandler {

        @ExceptionHandler(NotFoundException.class)
        public ResponseEntity<String> handleNotFoundException(HttpServletRequest req, NotFoundException ex) throws NotFoundException {
            return new ResponseEntity<String>(ex.getMessage(), HttpStatus.NOT_FOUND);
        }
    }
}

MicroService_2

The MicroService_2 calls MicroService_1 and catches the exception by HTTP code and regenerate the NotFoundException.

@Override
public MyEntity getMyEntity(Principal principal) {
    try {
        ResponseEntity<MyEntity> entityResponse = restTemplate.getForEntity(url, MyEntity.class, id);

        return entityResponse.getBody();

    } catch(HttpClientErrorException e) {
        HttpStatus status = e.getStatusCode();
        if (status == HttpStatus.NOT_FOUND) { 
            throw new NotFoundException(e.getResponseBodyAsString()); // should be "MyEntity [id: {id}] not found"
        } else {
            throw new UnexpectedServerException(e.getResponseBodyAsString());
        }
    }
}


回答3:

The Spring RestTemplate throws an error in case of 500 or 400 status codes. So if your second service responds with an error an exception will be thrown by the RestTemplate call in your first service.

  • HttpClientErrorException: in case of HTTP status 4xx
  • HttpServerErrorException: in case of HTTP status 5xx
  • UnknownHttpStatusCodeException: in case of an unknown HTTP status

To get the response message you could either catch the exception. E.g:

try {
    ResponseEntity<MyEntity> entityResponse = restTemplate.getForEntity(url, MyEntity.class, id);
} catch(HttpStatusCodeException e) {
    // e.getResponseBodyAsString();
}

or define a ResponseErrorHandler. The ResponseErrorHandler can be set during the instantiation of the RestTemplate. In the handleError method you will also be able to access the response message.

@Override
public void handleError(ClientHttpResponse httpResponse) 
    throws IOException {

}