Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RestTemplate response.getBody throws exception on 4** and 5** errors for put and post request but works fine for get requests

I am trying to intercept and log all the request-responses. To make requests i am using RestTemplate.exchange().

When i make a GET request and get an 4** error i can call the ClientHttpResponse.getBody() and can access the response body but for PUT and POST requests ClientHttpResponse.getBody() method throws an exception.

What might be causing this and how can i get the response body for POST and PUT requests as well?

This is where i make the request:

apiResponse = restTemplate.exchange(url, vCloudRequest.getHttpMethod(), entity, responseType);

This is the part of the interceptor that gets the exception:

@Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        ClientHttpResponse response = execution.execute(request, body);

        String requestString = new String(body);

        String responseString = new 
// Below line throws exception
String(ByteStreams.toByteArray(response.getBody()), Charset.forName("UTF-8"));

This is the stack.

Caused by: java.io.IOException: Server returned HTTP response code: 403 for URL: https://176.235.57.11/api/admin/org/bd154aaf-2e7c-446d-91be-f0a45138476b/users
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1876)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1474)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
    at org.springframework.http.client.SimpleClientHttpResponse.getBody(SimpleClientHttpResponse.java:85)
    at org.springframework.http.client.BufferingClientHttpResponseWrapper.getBody(BufferingClientHttpResponseWrapper.java:69)
    at roma.api_utils.model.Interceptors.RequestLoggingInterceptor.intercept(RequestLoggingInterceptor.java:39)
    at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:86)
    at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:70)
    at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
    at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:652)

Update :

When i call response.getStatusCode() before calling response.getBody() it doesn't throw IOException.

like image 717
Salih Erikci Avatar asked Sep 21 '25 08:09

Salih Erikci


1 Answers

Basic knowledge:

HttpURLConnection has two similar fields, errorStream and inputStream. When we invoke its getInputSteam method, it checks whether the response has an error code. If so, it throws an IOException and records it- that's why you got the exception. Furthermore, it also copies the contents in inputStream to errorStream, thus we can get its response body by invoking its getErrorStream method. This is exactly what SimpleClientHttpResponse does with its getBody method:

    @Override
    public InputStream getBody() throws IOException {
        InputStream errorStream = this.connection.getErrorStream();
        this.responseStream = 
(errorStream != null ? errorStream : this.connection.getInputStream());
        return this.responseStream;
    }

It first checks if errorStream is not null. If true, it returns it. If false, it calls connection.getInputStream() and returns that.

Now here are the answers

  1. Why does calling response.getBody() not throw an IOException after you called response.getStatusCode()? It is because getStatusCode calls getInputStream internally. Thus, errorStream will be not null when getBody is called.
  2. Why does it not throw an exception when the http method is GET? See method org.springframework.http.client.SimpleBufferingClientHttpRequest#executeInternal.

.

@Override
protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) 
throws IOException {
    addHeaders(this.connection, headers);
    // JDK <1.8 doesn't support getOutputStream with HTTP DELETE
    if (HttpMethod.DELETE == getMethod() && bufferedOutput.length == 0) {
        this.connection.setDoOutput(false);
    }
    if (this.connection.getDoOutput() && this.outputStreaming) {
        this.connection.setFixedLengthStreamingMode(bufferedOutput.length);
    }
    this.connection.connect();
    if (this.connection.getDoOutput()) {
        FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
    }
    else {
        // Immediately trigger the request in a no-output scenario as well
        this.connection.getResponseCode();
    }
    return new SimpleClientHttpResponse(this.connection);
}

It eagerly executes this.connection.getResponseCode(); when the http method is GET.

like image 163
neo Avatar answered Sep 22 '25 23:09

neo