1

I'm facing the following issue:

I'm making a call to another service using a simple org.springframework.web.client.RestTemplate

When calling it I need to intercept the request, modify the request body, and let it continue the flow. So far I don't have problems as using org.springframework.http.client.ClientHttpRequestInterceptor I can do whatever I want with my request (transforming RequestObjectA to requestObjectB before sending it to the service I'm calling).

Problem: How to modify the response body?

I see when calling ClientHttpResponse execute = clientHttpRequestExecution.execute(httpRequest, body) I can get the body doing execute.getBody(), so I can modify it, but I don't see a way to set it back somehow.

What could be a work around to set my modified body in a ClientHttpResponse?

Columb1a
  • 463
  • 2
  • 11
  • 25

4 Answers4

5

It will be required to return a new object of ClientHttpResponse bearing modified changes

We can copy the relevant data of originalResponse to the new modifiedResponse object created of ClientHttpResponse

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        
        ClientHttpResponse response = execution.execute(request, body);
        String responseBody = new String(response.getBody().readAllBytes());
        String modifiedBody = "Just Modified";

        ClientHttpResponse modifiedResponse = new ClientHttpResponse() {
            @Override
            public HttpHeaders getHeaders() {
                return response.getHeaders();
            }

            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream(modifiedBody.getBytes());
            }

            @Override
            public HttpStatus getStatusCode() throws IOException {
                return response.getStatusCode();
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return response.getRawStatusCode();
            }

            @Override
            public String getStatusText() throws IOException {
                return response.getStatusText();
            }

            @Override
            public void close() {

            }
        };
    return modifiedResponse;
}
Aditya Rewari
  • 2,343
  • 2
  • 23
  • 36
0

I know this is a little old, I was lazy and looking for an example of how to do as OP asked (with little luck) so ended up doing it myself.

The blow snippet wraps the ClientHttpResponse, it will lazily read the response body into a BAIS, this allows for reading the body multiple times (but does load the entire response body into memory). You may also replace the body using the setBody(...) method, replacing the response body will also modify the ContentLength header.

NB: This below code is untested.

public class ClientHttpResponseWrapper implements ClientHttpResponse{
    private final ClientHttpResponse response;
    private ByteArrayInputStream body;
    public ClientHttpResponseWrapper(ClientHttpResponse response) throws IOException {
        this.response=response;
        this.body=null;
    }

    public void setBody(ByteArrayInputStream body) {
        this.body = body;
    }
    @Override
    public ByteArrayInputStream getBody() throws IOException {
        if(this.body != null) {
            return this.body;
        }
        this.body=new ByteArrayInputStream(this.response.getBody().readAllBytes());
        return this.body;
    }
    
    @Override
    public HttpHeaders getHeaders() {
        if(this.body == null) {
            return this.response.getHeaders();
        }
        
        HttpHeaders out = new HttpHeaders(this.response.getHeaders());
        out.setContentLength(this.body.available());
        return out;
    }
    @Override
    public HttpStatus getStatusCode() throws IOException {
        return this.response.getStatusCode();
    }
    @Override
    public int getRawStatusCode() throws IOException {
        return this.response.getRawStatusCode();
    }
    @Override
    public String getStatusText() throws IOException {
        return this.response.getStatusText();
    }
    @Override
    public void close() {
        this.response.close();
    }
}
NeilA
  • 1,450
  • 2
  • 12
  • 20
-2

It seems you can modify the response also in the interceptor

public class RestTemplateResponseInterceptor implements ClientHttpRequestInterceptor {
  public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
    ClientHttpResponse response = execution.execute(request, body);
    return modifyResponse(response);
  }
}
Eugene
  • 154
  • 1
  • 2
  • 10
-9

Yes, this is tricky because the response body is an InputStream. An InputStream once read can not be read again depending upon type.

After reading the InputStream object for the first time, it may have reached the end of the Stream (EOFException) or the Stream has been closed when the InputStream object is read the second time. All the obtained data is null or no data is obtained.

You may try to look at MockClientHttpResponse https://www.gitmemory.com/issue/spring-projects/spring-framework/12671/771105006

PumpkinX
  • 67
  • 1