ResponseEntityExceptionHandler returns empty response body for 401 exceptions
Asked Answered
T

3

7

I'm trying to implement a Rest call to an authentication server using RestTemplate and log the response in case the server returns an exception. In order to do this, I used a ResponseEntityExceptionHandler to handle HttpClientErrorException's and HttpServerErrorException's.

public Response sendRequest(Request requestBody, String url, HttpMethod httpMethod, Response responseBody) {
    RestTemplate restTemplate = new RestTemplate();
    HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(requestBody.getBody(),
            this.securityHeaders);
    ResponseEntity<? extends Response> response = restTemplate.exchange(url, httpMethod, request,
            responseBody.getClass());
    return response.getBody();
}

Currently my ResponseEntityExceptionHandler is the following

@ControllerAdvice
public class RestResponseExceptionHandler extends ResponseEntityExceptionHandler {

private static Logger log = LoggerFactory.getLogger(RestResponseExceptionHandler.class);
private HttpHeaders headers = new HttpHeaders();

@ExceptionHandler(value = { HttpClientErrorException.class, HttpServerErrorException.class })
protected ResponseEntity<Object> handleConflict(HttpStatusCodeException ex, WebRequest request) {
    ErrorMessage responseBody = new ErrorMessage();
    try {
        JSONObject bodyOfException = new JSONObject(ex.getResponseBodyAsString());
        log.warn("{}| {}", ex.getStatusCode(), bodyOfException);
        responseBody.setErrorAndDescription(bodyOfException.optString("error"),
                bodyOfException.optString("error_description"));
    } catch (JSONException e) {
        log.error("{}| Bad authentication response body | Response Body | {}", ex.getStatusCode(),
                ex.getResponseBodyAsString());
    }

    return handleExceptionInternal(ex, responseBody, headers, ex.getStatusCode(), request);
}

}

However, only for HttpStatus 401 exceptions, I get an empty Response Body.

Example of logs:

400| {"error_description":"Bad client credentials","error":"invalid_client"}

401| Bad authentication response body | Response Body | 

Does anyone know how I can obtain the response body for 401 exceptions?

Edit: When using a Postman to call the Authentication Server I get non null 401 responseBody:

{
 "error": "invalid_token",
 "error_description": "Token was not recognised"
}
Thermosphere answered 28/2, 2019 at 18:25 Comment(2)
your log is with two different status code 400 and 401Droshky
Yes those are the logs for 2 different requests, one with a 401 response and another with a 400 responseThermosphere
T
16

I was finally able to log the response body for 401 Exceptions. I added the dependency

org.apache.httpcomponents:httpclient

and configured Rest Template Request Factory

    restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());

Apparently the default request factory SimpleClientHttpRequestFactory is not able to read the response body for 401 Http Status.

Thermosphere answered 1/3, 2019 at 17:17 Comment(3)
Thanks for your response, I've been struggling with this issue as well. Cheers!Rattan
Just a side help The class in question is docs.spring.io/spring-framework/docs/current/javadoc-api/org/…Ophthalmology
Works like a charm. How in the world did you figured this out.Sashasashay
B
1

Why do you believe an HttpStatusCodeException must have a response body? That is not the case.
Look at the constructor of RestClientResponseException, its direct superclass

 ...

 * @param responseBody the response body content (may be {@code null})
 * @param responseCharset the response body charset (may be {@code null})
 */
public RestClientResponseException(
        String message, 
        int statusCode,
        String statusText,
        @Nullable HttpHeaders responseHeaders, 
        @Nullable byte[] responseBody, 
        @Nullable Charset responseCharset) { ... }

You need to expect that case, and provide a custom response body.
The actual exception message is a good value to return as part of the body, which might be a complex object.

Bechler answered 28/2, 2019 at 18:37 Comment(5)
Yes I know, that the responseBody can be Null. However, when I use a Postman to call the authentication server and I get a receive a non null responseBodyThermosphere
@Thermosphere could you post the response you receive with a Postman call?Bechler
Yes, I added it to the original postThermosphere
@Thermosphere Look for DefaultResponseErrorHandler#handleError. Place a breakpoint there and look at the line "byte[] body = getResponseBody(response);"Bechler
Thank you, I did as you said and noticed that for 401 responses, the getResponseBody method was throwing an exception. Which lead me to the correct way around this. I posted the solution as a response in this post.Thermosphere
L
0

I was having a similar issue with a 400 response, and the above solution using the HttpComponentsClientHttpRequestFactory() did not work. I was able to read the body by using restTemplate.setFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory)); instead. Not sure if anybody is having my same problem but hopefully this helps if the chosen solution doesn't work for you.

Libbie answered 13/10, 2021 at 16:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.