Подтвердить что ты не робот

Spring RestTemplate - как включить полную отладку/протоколирование запросов/ответов?

Я использовал временную таблицу Spring RestTemplate, и я последовательно ударяю о стену, когда пытаюсь отлаживать ее запросы и ответы. Я в основном смотрю на то, что вижу, когда я использую завиток с включенной опцией "verbose". Например:

curl -v http://twitter.com/statuses/public_timeline.rss

Отображает как отправленные данные, так и полученные данные (включая заголовки, файлы cookie и т.д.).

Я проверил некоторые связанные сообщения, такие как: Как мне настроить ответ в Spring RestTemplate? но мне не удалось решить эту проблему.

Одним из способов сделать это было бы фактически изменить исходный код RestTemplate и добавить туда некоторые дополнительные протоколирующие заявления, но я бы нашел, что этот подход действительно является последним средством. Должен быть какой-то способ сообщить Spring веб-клиенту/RestTemplate вести журнал всего более дружелюбным способом.

Моя цель состояла бы в том, чтобы сделать это с помощью кода, например:

restTemplate.put("http://someurl", objectToPut, urlPathValues);

а затем получить тот же тип отладочной информации (как я получаю с curl) в файле журнала или в консоли. Я считаю, что это было бы чрезвычайно полезно для тех, кто использует

4b9b3361

Ответ 1

Наконец-то я нашел способ сделать это правильно. Большая часть решения исходит из Как настроить Spring и SLF4J, чтобы я мог регистрироваться?

Кажется, что нужно сделать две вещи:

  • Добавьте следующую строку в log4j.properties: log4j.logger.httpclient.wire=DEBUG
  • Убедитесь, что Spring не игнорирует конфигурацию ведения журнала

Вторая проблема происходит главным образом с средами Spring, где используется slf4j (как это было в моем случае). Таким образом, когда используется slf4j, убедитесь, что происходят следующие две вещи:

  • В вашем пути к классам нет библиотеки ведения общедоступных записей: это можно сделать, добавив дескрипторы исключения в ваш pom:

            <exclusions><exclusion>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
            </exclusion>
        </exclusions>
    
  • Файл log4j.properties хранится где-то в пути к классам, где Spring может найти/увидеть его. Если у вас возникли проблемы с этим, последним решением было бы поместить файл log4j.properties в пакет по умолчанию (не очень хорошая практика, а просто увидеть, что все работает так, как вы ожидаете)

Ответ 2

Просто чтобы завершить пример полной реализацией ClientHttpRequestInterceptor чтобы отследить запрос и ответ:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;

public class LoggingRequestInterceptor implements ClientHttpRequestInterceptor {

    final static Logger log = LoggerFactory.getLogger(LoggingRequestInterceptor.class);

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

    private void traceRequest(HttpRequest request, byte[] body) throws IOException {
        log.info("===========================request begin================================================");
        log.debug("URI         : {}", request.getURI());
        log.debug("Method      : {}", request.getMethod());
        log.debug("Headers     : {}", request.getHeaders() );
        log.debug("Request body: {}", new String(body, "UTF-8"));
        log.info("==========================request end================================================");
    }

    private void traceResponse(ClientHttpResponse response) throws IOException {
        StringBuilder inputStringBuilder = new StringBuilder();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(), "UTF-8"));
        String line = bufferedReader.readLine();
        while (line != null) {
            inputStringBuilder.append(line);
            inputStringBuilder.append('\n');
            line = bufferedReader.readLine();
        }
        log.info("============================response begin==========================================");
        log.debug("Status code  : {}", response.getStatusCode());
        log.debug("Status text  : {}", response.getStatusText());
        log.debug("Headers      : {}", response.getHeaders());
        log.debug("Response body: {}", inputStringBuilder.toString());
        log.info("=======================response end=================================================");
    }

}

Затем RestTemplate экземпляр RestTemplate используя BufferingClientHttpRequestFactory и LoggingRequestInterceptor:

RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(new LoggingRequestInterceptor());
restTemplate.setInterceptors(interceptors);

Требуется BufferingClientHttpRequestFactory поскольку мы хотим использовать тело ответа как в перехватчике, так и для исходного кода вызова. Реализация по умолчанию позволяет прочитать тело ответа только один раз.

Ответ 3

в Spring Загрузите, вы можете получить полный запрос/ответ, установив это в свойствах (или другой 12-факторный метод)

logging.level.org.apache.http=DEBUG

это выводит

-DEBUG .i.c.DefaultHttpClientConnectionOperator : Connecting to localhost/127.0.0.1:41827
-DEBUG .i.c.DefaultHttpClientConnectionOperator : Connection established 127.0.0.1:39546<->127.0.0.1:41827
-DEBUG o.a.http.impl.execchain.MainClientExec   : Executing request POST /v0/users HTTP/1.1
-DEBUG o.a.http.impl.execchain.MainClientExec   : Target auth state: UNCHALLENGED
-DEBUG o.a.http.impl.execchain.MainClientExec   : Proxy auth state: UNCHALLENGED
-DEBUG org.apache.http.headers                  : http-outgoing-0 >> POST /v0/users HTTP/1.1
-DEBUG org.apache.http.headers                  : http-outgoing-0 >> Content-Type: application/json;charset=UTF-8
-DEBUG org.apache.http.headers                  : http-outgoing-0 >> Content-Length: 56
-DEBUG org.apache.http.headers                  : http-outgoing-0 >> Host: localhost:41827
-DEBUG org.apache.http.headers                  : http-outgoing-0 >> Connection: Keep-Alive
-DEBUG org.apache.http.headers                  : http-outgoing-0 >> User-Agent: Apache-HttpClient/4.5.2 (Java/1.8.0_102)
-DEBUG org.apache.http.headers                  : http-outgoing-0 >> Accept-Encoding: gzip,deflate
-DEBUG org.apache.http.wire                     : http-outgoing-0 >> "POST /v0/users HTTP/1.1[\r][\n]"
-DEBUG org.apache.http.wire                     : http-outgoing-0 >> "Content-Type: application/json;charset=UTF-8[\r][\n]"
-DEBUG org.apache.http.wire                     : http-outgoing-0 >> "Content-Length: 56[\r][\n]"
-DEBUG org.apache.http.wire                     : http-outgoing-0 >> "Host: localhost:41827[\r][\n]"
-DEBUG org.apache.http.wire                     : http-outgoing-0 >> "Connection: Keep-Alive[\r][\n]"
-DEBUG org.apache.http.wire                     : http-outgoing-0 >> "User-Agent: Apache-HttpClient/4.5.2 (Java/1.8.0_102)[\r][\n]"
-DEBUG org.apache.http.wire                     : http-outgoing-0 >> "Accept-Encoding: gzip,deflate[\r][\n]"
-DEBUG org.apache.http.wire                     : http-outgoing-0 >> "[\r][\n]"
-DEBUG org.apache.http.wire                     : http-outgoing-0 >> "{"id":null,"email":"[email protected]","new":true}"

и ответ

-DEBUG .i.c.DefaultHttpClientConnectionOperator : Connecting to localhost/127.0.0.1:41827
-DEBUG .i.c.DefaultHttpClientConnectionOperator : Connection established 127.0.0.1:39546<->127.0.0.1:41827
-DEBUG o.a.http.impl.execchain.MainClientExec   : Executing request POST /v0/users HTTP/1.1
-DEBUG o.a.http.impl.execchain.MainClientExec   : Target auth state: UNCHALLENGED
-DEBUG o.a.http.impl.execchain.MainClientExec   : Proxy auth state: UNCHALLENGED
-DEBUG org.apache.http.headers                  : http-outgoing-0 >> POST /v0/users HTTP/1.1
-DEBUG org.apache.http.headers                  : http-outgoing-0 >> Content-Type: application/json;charset=UTF-8
-DEBUG org.apache.http.headers                  : http-outgoing-0 >> Content-Length: 56
-DEBUG org.apache.http.headers                  : http-outgoing-0 >> Host: localhost:41827
-DEBUG org.apache.http.headers                  : http-outgoing-0 >> Connection: Keep-Alive
-DEBUG org.apache.http.headers                  : http-outgoing-0 >> User-Agent: Apache-HttpClient/4.5.2 (Java/1.8.0_102)
-DEBUG org.apache.http.headers                  : http-outgoing-0 >> Accept-Encoding: gzip,deflate
-DEBUG org.apache.http.wire                     : http-outgoing-0 >> "POST /v0/users HTTP/1.1[\r][\n]"
-DEBUG org.apache.http.wire                     : http-outgoing-0 >> "Content-Type: application/json;charset=UTF-8[\r][\n]"
-DEBUG org.apache.http.wire                     : http-outgoing-0 >> "Content-Length: 56[\r][\n]"
-DEBUG org.apache.http.wire                     : http-outgoing-0 >> "Host: localhost:41827[\r][\n]"
-DEBUG org.apache.http.wire                     : http-outgoing-0 >> "Connection: Keep-Alive[\r][\n]"
-DEBUG org.apache.http.wire                     : http-outgoing-0 >> "User-Agent: Apache-HttpClient/4.5.2 (Java/1.8.0_102)[\r][\n]"
-DEBUG org.apache.http.wire                     : http-outgoing-0 >> "Accept-Encoding: gzip,deflate[\r][\n]"
-DEBUG org.apache.http.wire                     : http-outgoing-0 >> "[\r][\n]"
-DEBUG org.apache.http.wire                     : http-outgoing-0 >> "{"id":null,"email":"[email protected]","new":true}"

или просто logging.level.org.apache.http.wire=DEBUG, который, как представляется, содержит всю соответствующую информацию

Ответ 4

Расширение @hstoerr ответа с помощью некоторого кода:


Создать LoggingRequestInterceptor для ответов на запросы ответов

public class LoggingRequestInterceptor implements ClientHttpRequestInterceptor {

    private static final Logger log = LoggerFactory.getLogger(LoggingRequestInterceptor.class);

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {

        ClientHttpResponse response = execution.execute(request, body);

        log(request,body,response);

        return response;
    }

    private void log(HttpRequest request, byte[] body, ClientHttpResponse response) throws IOException {
        //do logging
    }
}

Настройка RestTemplate

RestTemplate rt = new RestTemplate();

//set interceptors/requestFactory
ClientHttpRequestInterceptor ri = new LoggingRequestInterceptor();
List<ClientHttpRequestInterceptor> ris = new ArrayList<ClientHttpRequestInterceptor>();
ris.add(ri);
rt.setInterceptors(ris);
rt.setRequestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory());

Ответ 5

Ни один из этих ответов фактически не разрешает 100% проблемы. mjj1409 получает большую часть этого, но удобно избегает проблемы регистрации ответа, что требует немного больше работы. Пол Сабу предлагает решение, которое кажется реалистичным, но не дает достаточно подробностей для фактического осуществления (и это не сработало для меня вообще). Sofiene получила регистрацию, но с критической проблемой: ответ больше не читается, потому что поток ввода уже был использован!

Я рекомендую использовать BufferingClientHttpResponseWrapper для обертывания объекта ответа, чтобы он несколько раз читал тело ответа:

public class LoggingRequestInterceptor implements ClientHttpRequestInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(LoggingRequestInterceptor.class);

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

        response = log(request, body, response);

        return response;
    }

    private ClientHttpResponse log(final HttpRequest request, final byte[] body, final ClientHttpResponse response) {
        final ClientHttpResponse responseCopy = new BufferingClientHttpResponseWrapper(response);
        logger.debug("Method: ", request.getMethod().toString());
        logger.debug("URI: ", , request.getURI().toString());
        logger.debug("Request Body: " + new String(body));
        logger.debug("Response body: " + IOUtils.toString(responseCopy.getBody()));
        return responseCopy;
    }

}

Это не будет потреблять InputStream, потому что тело ответа загружается в память и может быть прочитано несколько раз. Если у вас нет BufferingClientHttpResponseWrapper в вашем пути к классам, вы можете найти простую реализацию здесь:

https://github.com/spring-projects/spring-android/blob/master/spring-android-rest-template/src/main/java/org/springframework/http/client/BufferingClientHttpResponseWrapper.java

Для настройки RestTemplate:

LoggingRequestInterceptor loggingInterceptor = new LoggingRequestInterceptor();
restTemplate.getInterceptors().add(loggingInterceptor);

Ответ 6

Лучше всего добавить logging.level.org.springframework.web.client.RestTemplate=DEBUG в файл application.properties.

Другие решения, такие как установка log4j.logger.httpclient.wire, не всегда будут работать, потому что предполагают, что вы используете log4j и Apache HttpClient, что не всегда верно.

Обратите внимание, однако, что этот синтаксис будет работать только в последних версиях Spring Boot.

Ответ 7

Решение, данное ксенотрацидом для использования

logging.level.org.apache.http=DEBUG

хорош, но проблема в том, что по умолчанию Apache HttpComponents не используется.

Чтобы использовать Apache HttpComponents, добавьте в свой pom.xml

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpasyncclient</artifactId>
</dependency>

и настройте RestTemplate с помощью:

RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(new HttpComponentsAsyncClientHttpRequestFactory());

Ответ 8

Вы можете использовать spring-rest-template-logger для регистрации HTTP-трафика RestTemplate.

Добавьте зависимость в ваш проект Maven:

<dependency>
    <groupId>org.hobsoft.spring</groupId>
    <artifactId>spring-rest-template-logger</artifactId>
    <version>2.0.0</version>
</dependency>

Затем настройте RestTemplate следующим образом:

RestTemplate restTemplate = new RestTemplateBuilder()
    .customizers(new LoggingCustomizer())
    .build()

Теперь весь HTTP-трафик RestTemplate будет регистрироваться в org.hobsoft.spring.resttemplatelogger.LoggingCustomizer на уровне отладки.

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я написал эту библиотеку.

Ответ 9

Вход в систему RestTemplate

Вариант 1. Откройте журнал отладки.

Конфигурирование RestTemplate

  • По умолчанию RestTemplate использует стандартные возможности JDK для установления HTTP-соединений. Вы можете переключиться на использование другой библиотеки HTTP, такой как Apache HttpComponents

    @Bean public RestTemplate restTemplate (построитель RestTemplateBuilder) {RestTemplate restTemplate = builder.build(); return restTemplate; return restTemplate; } }

Конфигурирование регистрации

  • application.yml

    logging: level: org.springframework.web.client.RestTemplate: DEBUG

Вариант 2. Использование перехватчика

Ответ обертки

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.StreamUtils;

public final class BufferingClientHttpResponseWrapper implements ClientHttpResponse {

    private final ClientHttpResponse response;

    private byte[] body;


    BufferingClientHttpResponseWrapper(ClientHttpResponse response) {
        this.response = response;
    }

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

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

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

    public HttpHeaders getHeaders() {
        return this.response.getHeaders();
    }

    public InputStream getBody() throws IOException {
        if (this.body == null) {
            this.body = StreamUtils.copyToByteArray(this.response.getBody());
        }
        return new ByteArrayInputStream(this.body);
    }

    public void close() {
        this.response.close();
    }
}

Внедрить перехватчик

package com.example.logging;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;

public class LoggingRestTemplate implements ClientHttpRequestInterceptor {

    private final static Logger LOGGER = LoggerFactory.getLogger(LoggingRestTemplate.class);

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

    private void traceRequest(HttpRequest request, byte[] body) throws IOException {
        if (!LOGGER.isDebugEnabled()) {
            return;
        }
        LOGGER.debug(
                "==========================request begin==============================================");
        LOGGER.debug("URI                 : {}", request.getURI());
        LOGGER.debug("Method            : {}", request.getMethod());
        LOGGER.debug("Headers         : {}", request.getHeaders());
        LOGGER.debug("Request body: {}", new String(body, "UTF-8"));
        LOGGER.debug(
                "==========================request end================================================");
    }

    private ClientHttpResponse traceResponse(ClientHttpResponse response) throws IOException {
        if (!LOGGER.isDebugEnabled()) {
            return response;
        }
        final ClientHttpResponse responseWrapper = new BufferingClientHttpResponseWrapper(response);
        StringBuilder inputStringBuilder = new StringBuilder();
        BufferedReader bufferedReader = new BufferedReader(
                new InputStreamReader(responseWrapper.getBody(), "UTF-8"));
        String line = bufferedReader.readLine();
        while (line != null) {
            inputStringBuilder.append(line);
            inputStringBuilder.append('\n');
            line = bufferedReader.readLine();
        }
        LOGGER.debug(
                "==========================response begin=============================================");
        LOGGER.debug("Status code    : {}", responseWrapper.getStatusCode());
        LOGGER.debug("Status text    : {}", responseWrapper.getStatusText());
        LOGGER.debug("Headers            : {}", responseWrapper.getHeaders());
        LOGGER.debug("Response body: {}", inputStringBuilder.toString());
        LOGGER.debug(
                "==========================response end===============================================");
        return responseWrapper;
    }

}

Конфигурирование RestTemplate

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
    RestTemplate restTemplate = builder.build();
    restTemplate.setInterceptors(Collections.singletonList(new LoggingRestTemplate()));
    return restTemplate;
}

Конфигурирование регистрации

  • Проверьте пакет LoggingRestTemplate, например, в application.yml:

    logging: level: com.example.logging: DEBUG

Вариант 3. Использование httpcomponent

Импортировать зависимость httpcomponent

<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpasyncclient</artifactId>

Конфигурирование RestTemplate

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
    RestTemplate restTemplate = builder.build();
    restTemplate.setRequestFactory(new HttpComponentsAsyncClientHttpRequestFactory());
    return restTemplate;
}

Конфигурирование регистрации

  • Проверьте пакет LoggingRestTemplate, например, в application.yml:

    logging: уровень: org.apache.http: DEBUG

Ответ 10

Помимо протокола HttpClient, описанного в другом ответе, вы также можете ввести ClientHttpRequestInterceptor, который считывает тело запроса и отвечает и регистрирует его. Возможно, вы захотите сделать это, если другой материал также использует HttpClient, или если вы хотите использовать собственный формат ведения журнала. Предостережение: вам нужно предоставить RestTemplate BufferingClientHttpRequestFactory, чтобы вы могли прочитать ответ дважды.

Ответ 11

Как указано в других ответах, орган реагирования нуждается в специальном лечении, поэтому его можно прочитать многократно (по умолчанию его содержимое потребляется при первом чтении).

Вместо того, чтобы использовать BufferingClientHttpRequestFactory при настройке запроса, сам перехватчик может обернуть ответ и убедиться, что содержимое сохранено и может быть повторно прочитано (как регистратором, так и потребителем ответа):

Мой перехватчик, который

  • буферизирует тело ответа с помощью обертки
  • журналы более компактны
  • также регистрирует идентификатор кода состояния (например, 201 создан)
  • включает номер последовательности запросов, позволяющий легко отличать параллельные записи журнала от нескольких потоков

Код:

public class LoggingInterceptor implements ClientHttpRequestInterceptor {

    private final Logger log = LoggerFactory.getLogger(getClass());
    private AtomicInteger requestNumberSequence = new AtomicInteger(0);

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        int requestNumber = requestNumberSequence.incrementAndGet();
        logRequest(requestNumber, request, body);
        ClientHttpResponse response = execution.execute(request, body);
        response = new BufferedClientHttpResponse(response);
        logResponse(requestNumber, response);
        return response;
    }

    private void logRequest(int requestNumber, HttpRequest request, byte[] body) {
        if (log.isDebugEnabled()) {
            String prefix = requestNumber + " > ";
            log.debug("{} Request: {} {}", prefix, request.getMethod(), request.getURI());
            log.debug("{} Headers: {}", prefix, request.getHeaders());
            if (body.length > 0) {
                log.debug("{} Body: \n{}", prefix, new String(body, StandardCharsets.UTF_8));
            }
        }
    }

    private void logResponse(int requestNumber, ClientHttpResponse response) throws IOException {
        if (log.isDebugEnabled()) {
            String prefix = requestNumber + " < ";
            log.debug("{} Response: {} {} {}", prefix, response.getStatusCode(), response.getStatusCode().name(), response.getStatusText());
            log.debug("{} Headers: {}", prefix, response.getHeaders());
            String body = StreamUtils.copyToString(response.getBody(), StandardCharsets.UTF_8);
            if (body.length() > 0) {
                log.debug("{} Body: \n{}", prefix, body);
            }
        }
    }

    /**
     * Wrapper around ClientHttpResponse, buffers the body so it can be read repeatedly (for logging & consuming the result).
     */
    private static class BufferedClientHttpResponse implements ClientHttpResponse {

        private final ClientHttpResponse response;
        private byte[] body;

        public BufferedClientHttpResponse(ClientHttpResponse response) {
            this.response = response;
        }

        @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() {
            response.close();
        }

        @Override
        public InputStream getBody() throws IOException {
            if (body == null) {
                body = StreamUtils.copyToByteArray(response.getBody());
            }
            return new ByteArrayInputStream(body);
        }

        @Override
        public HttpHeaders getHeaders() {
            return response.getHeaders();
        }
    }
}

Конфигурация:

 @Bean
    public RestTemplateBuilder restTemplateBuilder() {
        return new RestTemplateBuilder()
                .additionalInterceptors(Collections.singletonList(new LoggingInterceptor()));
    }

Пример вывода журнала:

2018-10-08 10:58:53 [main] DEBUG x.y.z.LoggingInterceptor - 2 >  Request: POST http://localhost:53969/payment/v4/private/payment-lists/10022/templates
2018-10-08 10:58:53 [main] DEBUG x.y.z.LoggingInterceptor - 2 >  Headers: {Accept=[application/json, application/*+json], Content-Type=[application/json;charset=UTF-8], Content-Length=[986]}
2018-10-08 10:58:53 [main] DEBUG x.y.z.LoggingInterceptor - 2 >  Body: 
{"idKey":null, ...}
2018-10-08 10:58:53 [main] DEBUG x.y.z.LoggingInterceptor - 2 <  Response: 200 OK 
2018-10-08 10:58:53 [main] DEBUG x.y.z.LoggingInterceptor - 2 <  Headers: {Content-Type=[application/json;charset=UTF-8], Transfer-Encoding=[chunked], Date=[Mon, 08 Oct 2018 08:58:53 GMT]}
2018-10-08 10:58:53 [main] DEBUG x.y.z.LoggingInterceptor - 2 <  Body: 
{ "idKey" : "10022", ...  }

Ответ 12

Предполагая, что RestTemplate настроен на использование HttpClient 4.x, вы можете прочитать здесь документацию по протоколу HttpClient. Регистраторы отличаются от тех, которые указаны в других ответах.

Конфигурация регистрации для HttpClient 3.x доступна здесь.

Ответ 13

application.properties

logging.level.org.springframework.web.client=DEBUG

application.yml

logging:
  level:  
    root: WARN
    org.springframework.web.client: DEBUG

Ответ 14

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

Добавив ниже 2 строки, application.properties регистрирует все запросы и ответы 1-го строчка, чтобы регистрировать запросы и 2-я строка для регистрации ответов.

logging.level.org.springframework.web.client.RestTemplate=DEBUG logging.level.org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor=DEBUG

Ответ 15

---- июль 2019 года ----

(с использованием Spring Boot)

Я был удивлен, что Spring Boot, со всей его магией Zero Configuration, не предоставляет простой способ проверки или регистрации простого тела ответа JSON с RestTemplate. Я просмотрел различные ответы и комментарии, представленные здесь, и делюсь своей собственной дистиллированной версией того, что (все еще) работает, и мне кажется разумным решением, учитывая текущие параметры (я использую Spring Boot 2.1.6 с Gradle 4.4 )

1. Использование Fiddler в качестве http-прокси

На самом деле это довольно элегантное решение, поскольку оно обходит все громоздкие усилия по созданию собственного перехватчика или замене базового http-клиента на apache (см. ниже).

Install и run Fiddler

а затем

добавьте -DproxySet=true -Dhttp.proxyHost=localhost -Dhttp.proxyPort=8888 к своим параметрам виртуальной машины

2. Использование Apache HttpClient

Добавьте Apache HttpClient в зависимости от Maven или Gradle.

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.9</version>
</dependency>

Используйте HttpComponentsClientHttpRequestFactory в качестве RequestFactory для RestTemplate. Самый простой способ сделать это:

RestTemplate restTemplate = new RestTemplate();

restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());

Включите DEBUG в своем файле application.properties (если вы используете Spring Boot)

logging.level.org.apache.http=DEBUG

Если вы используете Spring Boot, вам необходимо убедиться, что у вас настроена среда ведения журналов, например с помощью зависимости spring-boot-starter, которая включает в себя spring-boot-starter-logging.

3. Используйте перехватчик

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

4. Записать URL и статус ответа без тела

Хотя это не соответствует заявленным требованиям регистрации тела, это быстрый и простой способ начать регистрацию ваших вызовов REST. Он отображает полный URL-адрес и статус ответа.

Просто добавьте следующую строку в ваш файл application.properties (при условии, что вы используете Spring Boot, и если вы используете зависимость для запуска пружинной загрузки, которая включает в себя spring-boot-starter-logging)

logging.level.org.springframework.web.client.RestTemplate=DEBUG

Вывод будет выглядеть примерно так:

2019-07-29 11:53:50.265 DEBUG o.s.web.client.RestTemplate : HTTP GET http://www.myrestservice.com/Endpoint?myQueryParam=myValue
2019-07-29 11:53:50.276 DEBUG o.s.web.client.RestTemplate : Accept=[application/json]
2019-07-29 11:53:50.584 DEBUG o.s.web.client.RestTemplate : Response 200 OK
2019-07-29 11:53:50.585 DEBUG o.s.web.client.RestTemplate : Reading to [org.mynamespace.MyJsonModelClass]

Ответ 16

Так много ответов здесь требуют изменений в кодировке и настраиваемых классов, и это действительно не обязательно. Gte прокси-сервер отладки, такой как fiddler, и установите среду java для использования прокси-сервера в командной строке (-Dhttp.proxyHost и -Dhttp.proxyPort), затем запустите скрипт, и вы сможете полностью просмотреть запросы и ответы. Также поставляется множество дополнительных преимуществ, таких как возможность возиться с результатами и ответами до и после их отправки для запуска экспериментов, прежде чем перейти к модификации сервера.

Последний бит проблемы может возникнуть, если вы должны использовать HTTPS, вам нужно будет экспортировать сертификат SSL из скрипта и импортировать его в подсказку java keystore (cacerts): по умолчанию пароль java keystore по умолчанию является "changeit".

Ответ 17

Трюк настройки RestTemplate с помощью BufferingClientHttpRequestFactory не работает, если вы используете любой ClientHttpRequestInterceptor, который вы будете использовать, если пытаетесь выполнить регистрацию через перехватчики. Это связано с тем, что InterceptingHttpAccessor (подклассы RestTemplate) работают.

Короче говоря, просто используйте этот класс вместо RestTemplate (обратите внимание, что он использует API регистрации SLF4J, при необходимости отредактируйте):

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.RestTemplate;

/**
 * A {@link RestTemplate} that logs every request and response.
 */
public class LoggingRestTemplate extends RestTemplate {

    // Bleh, this class is not public
    private static final String RESPONSE_WRAPPER_CLASS = "org.springframework.http.client.BufferingClientHttpResponseWrapper";

    private Logger log = LoggerFactory.getLogger(this.getClass());

    private boolean hideAuthorizationHeaders = true;
    private Class<?> wrapperClass;
    private Constructor<?> wrapperConstructor;

    /**
     * Configure the logger to log requests and responses to.
     *
     * @param log log destination, or null to disable
     */
    public void setLogger(Logger log) {
        this.log = log;
    }

    /**
     * Configure the logger to log requests and responses to by name.
     *
     * @param name name of the log destination, or null to disable
     */
    public void setLoggerName(String name) {
        this.setLogger(name != null ? LoggerFactory.getLogger(name) : null);
    }

    /**
     * Configure whether to hide the contents of {@code Authorization} headers.
     *
     * <p>
     * Default true.
     *
     * @param hideAuthorizationHeaders true to hide, otherwise false
     */
    public void setHideAuthorizationHeaders(boolean hideAuthorizationHeaders) {
        this.hideAuthorizationHeaders = hideAuthorizationHeaders;
    }

    /**
     * Log a request.
     */
    protected void traceRequest(HttpRequest request, byte[] body) {
        this.log.debug("xmit: {} {}\n{}{}", request.getMethod(), request.getURI(), this.toString(request.getHeaders()),
          body != null && body.length > 0 ? "\n\n" + new String(body, StandardCharsets.UTF_8) : "");
    }

    /**
     * Log a response.
     */
    protected void traceResponse(ClientHttpResponse response) {
        final ByteArrayOutputStream bodyBuf = new ByteArrayOutputStream();
        HttpStatus statusCode = null;
        try {
            statusCode = response.getStatusCode();
        } catch (IOException e) {
            // ignore
        }
        String statusText = null;
        try {
            statusText = response.getStatusText();
        } catch (IOException e) {
            // ignore
        }
        try (final InputStream input = response.getBody()) {
            byte[] b = new byte[1024];
            int r;
            while ((r = input.read(b)) != -1)
                bodyBuf.write(b, 0, r);
        } catch (IOException e) {
            // ignore
        }
        this.log.debug("recv: {} {}\n{}{}", statusCode, statusText, this.toString(response.getHeaders()),
          bodyBuf.size() > 0 ? "\n\n" + new String(bodyBuf.toByteArray(), StandardCharsets.UTF_8) : "");
    }

    @PostConstruct
    private void addLoggingInterceptor() {
        this.getInterceptors().add(new ClientHttpRequestInterceptor() {
            @Override
            public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
              throws IOException {

                // Log request
                if (LoggingRestTemplate.this.log != null && LoggingRestTemplate.this.log.isDebugEnabled())
                    LoggingRestTemplate.this.traceRequest(request, body);

                // Perform request
                ClientHttpResponse response = execution.execute(request, body);

                // Log response
                if (LoggingRestTemplate.this.log != null && LoggingRestTemplate.this.log.isDebugEnabled()) {
                    final ClientHttpResponse bufferedResponse = LoggingRestTemplate.this.ensureBuffered(response);
                    if (bufferedResponse != null) {
                        LoggingRestTemplate.this.traceResponse(bufferedResponse);
                        response = bufferedResponse;
                    }
                }

                // Done
                return response;
            }
        });
    }

    private ClientHttpResponse ensureBuffered(ClientHttpResponse response) {
        try {
            if (this.wrapperClass == null)
                this.wrapperClass = Class.forName(RESPONSE_WRAPPER_CLASS, false, ClientHttpResponse.class.getClassLoader());
            if (!this.wrapperClass.isInstance(response)) {
                if (this.wrapperConstructor == null) {
                    this.wrapperConstructor = this.wrapperClass.getDeclaredConstructor(ClientHttpResponse.class);
                    this.wrapperConstructor.setAccessible(true);
                }
                response = (ClientHttpResponse)this.wrapperConstructor.newInstance(response);
            }
            return response;
        } catch (Exception e) {
            this.log.error("error creating {} instance: {}", RESPONSE_WRAPPER_CLASS, e);
            return null;
        }
    }

    private String toString(HttpHeaders headers) {
        final StringBuilder headerBuf = new StringBuilder();
        for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
            if (headerBuf.length() > 0)
                headerBuf.append('\n');
            final String name = entry.getKey();
            for (String value : entry.getValue()) {
                if (this.hideAuthorizationHeaders && name.equalsIgnoreCase(HttpHeaders.AUTHORIZATION))
                    value = "[omitted]";
                headerBuf.append(name).append(": ").append(value);
            }
        }
        return headerBuf.toString();
    }
}

Я согласен с этим глупо, что для этого требуется много работы.

Ответ 18

Добавление к предыдущему обсуждению представляет собой только счастливые сценарии. вероятно, вы не сможете зарегистрировать ответ, если появится Ошибка.

В этом случае плюс все вышеперечисленные случаи вы должны переопределить DefaultResponseErrorHandler и установить его, как показано ниже

restTemplate.setErrorHandler(new DefaultResponseErrorHandlerImpl());

Ответ 19

Как ни странно, ни одно из этих решений не работает, поскольку RestTemplate, похоже, не возвращает ответ на некоторые ошибки клиента и сервера 500x. В этом случае вы также будете регистрировать их, выполнив ResponseErrorHandler следующим образом. Вот код проекта, но вы понимаете:

Вы можете установить тот же самый перехватчик, что и обработчик ошибок:

restTemplate.getInterceptors().add(interceptor);
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
restTemplate.setErrorHandler(interceptor);

И перехват реализует оба интерфейса:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus.Series;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.ResponseErrorHandler;

public class LoggingRequestInterceptor implements ClientHttpRequestInterceptor, ResponseErrorHandler {
    static final Logger log = LoggerFactory.getLogger(LoggingRequestInterceptor.class);
    static final DefaultResponseErrorHandler defaultResponseErrorHandler = new DefaultResponseErrorHandler();
    final Set<Series> loggableStatuses = new HashSet();

    public LoggingRequestInterceptor() {
    }

    public LoggingRequestInterceptor(Set<Series> loggableStatuses) {
        loggableStatuses.addAll(loggableStatuses);
    }

    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        this.traceRequest(request, body);
        ClientHttpResponse response = execution.execute(request, body);
        if(response != null) {
            this.traceResponse(response);
        }

        return response;
    }

    private void traceRequest(HttpRequest request, byte[] body) throws IOException {
        log.debug("===========================request begin================================================");
        log.debug("URI         : {}", request.getURI());
        log.debug("Method      : {}", request.getMethod());
        log.debug("Headers     : {}", request.getHeaders());
        log.debug("Request body: {}", new String(body, "UTF-8"));
        log.debug("==========================request end================================================");
    }

    private void traceResponse(ClientHttpResponse response) throws IOException {
        if(this.loggableStatuses.isEmpty() || this.loggableStatuses.contains(response.getStatusCode().series())) {
            StringBuilder inputStringBuilder = new StringBuilder();

            try {
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(), "UTF-8"));

                for(String line = bufferedReader.readLine(); line != null; line = bufferedReader.readLine()) {
                    inputStringBuilder.append(line);
                    inputStringBuilder.append('\n');
                }
            } catch (Throwable var5) {
                log.error("cannot read response due to error", var5);
            }

            log.debug("============================response begin==========================================");
            log.debug("Status code  : {}", response.getStatusCode());
            log.debug("Status text  : {}", response.getStatusText());
            log.debug("Headers      : {}", response.getHeaders());
            log.debug("Response body: {}", inputStringBuilder.toString());
            log.debug("=======================response end=================================================");
        }

    }

    public boolean hasError(ClientHttpResponse response) throws IOException {
        return defaultResponseErrorHandler.hasError(response);
    }

    public void handleError(ClientHttpResponse response) throws IOException {
        this.traceResponse(response);
        defaultResponseErrorHandler.handleError(response);
    }
}

Ответ 20

Лучшее решение теперь просто добавьте зависимость:

<dependency>
  <groupId>com.github.zg2pro</groupId>
  <artifactId>spring-rest-basis</artifactId>
  <version>v.x</version>
</dependency>

Он содержит класс LoggingRequestInterceptor, который вы можете добавить в свой RestTemplate:

интегрируйте эту утилиту, добавив ее в качестве перехватчика в spring RestTemplate следующим образом:

restTemplate.setRequestFactory(LoggingRequestFactoryFactory.build());

и добавьте реализацию slf4j в вашу инфраструктуру, такую ​​как log4j.

или напрямую использовать "Zg2proRestTemplate" . "Лучший ответ" @PaulSabou выглядит так, потому что httpclient и все apache.http libs не обязательно загружаются при использовании spring RestTemplate.

Ответ 21

Хотел добавить и мою реализацию этого. Прошу прощения за все недостающие полуколоны, это написано в Groovy.

Мне нужно что-то более настраиваемое, чем принятый ответ. Здесь компонент шаблона останова, который очень проворный и будет регистрировать все, что ищет OP.

Пользовательский класс перехватчиков регистрации:

import org.springframework.http.HttpRequest
import org.springframework.http.client.ClientHttpRequestExecution
import org.springframework.http.client.ClientHttpRequestInterceptor
import org.springframework.http.client.ClientHttpResponse
import org.springframework.util.StreamUtils

import java.nio.charset.Charset

class HttpLoggingInterceptor implements ClientHttpRequestInterceptor {

    private final static Logger log = LoggerFactory.getLogger(HttpLoggingInterceptor.class)

    @Override
    ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        logRequest(request, body)
        ClientHttpResponse response = execution.execute(request, body)
        logResponse(response)
        return response
    }

    private void logRequest(HttpRequest request, byte[] body) throws IOException {
        if (log.isDebugEnabled()) {
            log.debug("===========================request begin================================================")
            log.debug("URI         : {}", request.getURI())
            log.debug("Method      : {}", request.getMethod())
            log.debug("Headers     : {}", request.getHeaders())
            log.debug("Request body: {}", new String(body, "UTF-8"))
            log.debug("==========================request end================================================")
        }
    }

    private void logResponse(ClientHttpResponse response) throws IOException {
        if (log.isDebugEnabled()) {
            log.debug("============================response begin==========================================")
            log.debug("Status code  : {}", response.getStatusCode())
            log.debug("Status text  : {}", response.getStatusText())
            log.debug("Headers      : {}", response.getHeaders())
            log.debug("Response body: {}", StreamUtils.copyToString(response.getBody(), Charset.defaultCharset()))
            log.debug("=======================response end=================================================")
        }
    }
}

Определение шаблона блогов:

@Bean(name = 'myRestTemplate')
RestTemplate myRestTemplate(RestTemplateBuilder builder) {

    RequestConfig requestConfig = RequestConfig.custom()
            .setConnectTimeout(10 * 1000) // 10 seconds
            .setSocketTimeout(300 * 1000) // 300 seconds
            .build()

    PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager()
    connectionManager.setMaxTotal(10)
    connectionManager.closeIdleConnections(5, TimeUnit.MINUTES)

    CloseableHttpClient httpClient = HttpClients.custom()
            .setConnectionManager(connectionManager)
            .setDefaultRequestConfig(requestConfig)
            .disableRedirectHandling()
            .build()

    RestTemplate restTemplate = builder
            .rootUri("https://domain.server.com")
            .basicAuthorization("username", "password")
            .requestFactory(new BufferingClientHttpRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient)))
            .interceptors(new HttpLoggingInterceptor())
            .build()

    return restTemplate
}

Реализация:

@Component
class RestService {

    private final RestTemplate restTemplate
    private final static Logger log = LoggerFactory.getLogger(RestService.class)

    @Autowired
    RestService(
            @Qualifier("myRestTemplate") RestTemplate restTemplate
    ) {
        this.restTemplate = restTemplate
    }

    // add specific methods to your service that access the GET and PUT methods

    private <T> T getForObject(String path, Class<T> object, Map<String, ?> params = [:]) {
        try {
            return restTemplate.getForObject(path, object, params)
        } catch (HttpClientErrorException e) {
            log.warn("Client Error (${path}): ${e.responseBodyAsString}")
        } catch (HttpServerErrorException e) {
            String msg = "Server Error (${path}): ${e.responseBodyAsString}"
            log.error(msg, e)
        } catch (RestClientException e) {
            String msg = "Error (${path})"
            log.error(msg, e)
        }
        return null
    }

    private <T> T putForObject(String path, T object) {
        try {
            HttpEntity<T> request = new HttpEntity<>(object)
            HttpEntity<T> response = restTemplate.exchange(path, HttpMethod.PUT, request, T)
            return response.getBody()
        } catch (HttpClientErrorException e) {
            log.warn("Error (${path}): ${e.responseBodyAsString}")
        } catch (HttpServerErrorException e) {
            String msg = "Error (${path}): ${e.responseBodyAsString}"
            log.error(msg, e)
        } catch (RestClientException e) {
            String msg = "Error (${path})"
            log.error(msg, e)
        }
        return null
    }
}

Ответ 23

Как отметил @MilacH, в реализации есть ошибка. Если возвращается статусCode> 400, генерируется исключение IOException, так как errorHandler не вызывается из перехватчиков. Исключение может быть проигнорировано и затем снова поймано в методе обработчика.

package net.sprd.fulfillment.common;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import static java.nio.charset.StandardCharsets.UTF_8;

public class LoggingRequestInterceptor implements ClientHttpRequestInterceptor {

    final static Logger log = LoggerFactory.getLogger(LoggingRequestInterceptor.class);

    @SuppressWarnings("HardcodedLineSeparator")
    public static final char LINE_BREAK = '\n';

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        try {
            traceRequest(request, body);
        } catch (Exception e) {
            log.warn("Exception in LoggingRequestInterceptor while tracing request", e);
        }

        ClientHttpResponse response = execution.execute(request, body);

        try {
            traceResponse(response);
        } catch (IOException e) {
            // ignore the exception here, as it will be handled by the error handler of the restTemplate
            log.warn("Exception in LoggingRequestInterceptor", e);
        }
        return response;
    }

    private void traceRequest(HttpRequest request, byte[] body) {
        log.info("===========================request begin================================================");
        log.info("URI         : {}", request.getURI());
        log.info("Method      : {}", request.getMethod());
        log.info("Headers     : {}", request.getHeaders());
        log.info("Request body: {}", new String(body, UTF_8));
        log.info("==========================request end================================================");
    }

    private void traceResponse(ClientHttpResponse response) throws IOException {
        StringBuilder inputStringBuilder = new StringBuilder();
        try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(), UTF_8))) {
            String line = bufferedReader.readLine();
            while (line != null) {
                inputStringBuilder.append(line);
                inputStringBuilder.append(LINE_BREAK);
                line = bufferedReader.readLine();
            }
        }

        log.info("============================response begin==========================================");
        log.info("Status code  : {}", response.getStatusCode());
        log.info("Status text  : {}", response.getStatusText());
        log.info("Headers      : {}", response.getHeaders());
        log.info("Response body: {}", inputStringBuilder);
        log.info("=======================response end=================================================");
    }

}

Ответ 24

Для входа в Logback с помощью Apache HttpClient:

Вам нужен Apache HttpClient в classpath:

<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpclient</artifactId>
  <version>4.5.10</version>
</dependency>

Настройте RestTemplate на использование HttpClient:

restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());

Чтобы регистрировать запросы и ответы, добавьте в файл конфигурации Logback:

<logger name="org.apache.http.wire" level="DEBUG"/>

Или войти еще больше:

<logger name="org.apache.http" level="DEBUG"/>

Ответ 25

Что касается ответа с использованием ClientHttpInterceptor, я нашел способ сохранить весь ответ без буферизационных заводов. Просто сохраните поток входных данных ответа внутри байтового массива, используя некоторый метод utils, который скопирует этот массив из тела, но важно, окружайте этот метод с помощью try catch, потому что он сломается, если ответ пуст (что является причиной исключения Access Access) и в catch просто создайте пустой массив байтов, а не просто создайте анонимный внутренний класс ClientHttpResponse, используя этот массив и другие параметры из исходного ответа. Затем вы можете вернуть этот новый объект ClientHttpResponse в цепочку выполнения остальных поколений, и вы можете регистрировать ответ, используя массив байтов тела, который ранее был сохранен. Таким образом, вы избежите использования InputStream в действительном ответе, и вы можете использовать ответ Rest Template как есть. Обратите внимание, что это может быть опасно, если ваш ответ слишком велик

Ответ 26

моя конфигурация logger используется xml

<logger name="org.springframework.web.client.RestTemplate">
    <level value="trace"/>
</logger>

то вы получите что-то вроде ниже:

DEBUG org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:92) : Reading [com.test.java.MyClass] as "application/json" using [org.springfr[email protected]604525f1]

через HttpMessageConverterExtractor.java:92, вам нужно продолжить отладку, и в моем случае я получил следующее:

genericMessageConverter.write(requestBody, requestBodyType, requestContentType, httpRequest);

и это:

outputMessage.getBody().flush();

outputMessage.getBody() содержит сообщение http (post type) отправляет