Измените тело ответа при реализации ClientHttpRequestInterceptor

Я столкнулся со следующей проблемой:

Я звоню в другую службу с помощью простого org.springframework.web.client.RestTemplate

При его вызове мне нужно перехватить запрос, изменить тело запроса и позволить ему продолжить выполнение. Пока у меня нет проблем с использованиемorg.springframework.http.client.ClientHttpRequestInterceptor Я могу делать со своим запросом все, что захочу (преобразовывать RequestObjectA в requestObjectB перед отправкой в ​​службу, которую я вызываю).

Проблема: как изменить тело ответа?

Я вижу когда звоню ClientHttpResponse execute = clientHttpRequestExecution.execute(httpRequest, body) Я могу заставить тело делать execute.getBody(), поэтому я могу его изменить, но я не вижу способа как-то вернуть его обратно.

Что может быть работа, чтобы установить мое измененное тело в ClientHttpResponse?

4 ответа

Потребуется вернуть новый объект ClientHttpResponse несущие модифицированные изменения

Мы можем скопировать соответствующие данные из originalResponse в новый объект modifiedResponse, созданный 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;
}

Я знаю, что это немного устарело, я был ленив и искал пример того, как сделать, как попросил OP (с небольшой удачей), поэтому в конечном итоге сделал это сам.

Фрагмент удара обертывает ClientHttpResponse, он лениво считывает тело ответа в BAIS, это позволяет читать тело несколько раз (но загружает все тело ответа в память). Вы также можете заменить тело с помощью метода setBody(...), замена тела ответа также изменит заголовок ContentLength.

NB: приведенный ниже код не тестировался.

      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();
    }
}

Да, это сложно, потому что тело ответа - это InputStream. Однажды прочитанный InputStream не может быть прочитан снова в зависимости от типа.

После первого чтения объекта InputStream, возможно, он достиг конца потока (EOFException) или поток был закрыт при втором чтении объекта InputStream. Все полученные данные равны нулю или данные не получены.

Вы можете попробовать посмотреть MockClientHttpResponsehttps://www.gitmemory.com/issue/spring-projects/spring-framework/12671/771105006

Кажется, вы можете изменить ответ также в перехватчике

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);
  }
}
Другие вопросы по тегам