Spring RestTemplate - как включить полную отладку / запись запросов / ответов? - PullRequest
175 голосов
/ 31 октября 2011

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

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

будет отображать как отправленные данные, так и полученные данные (включая заголовки, файлы cookie и т.Как мне зарегистрировать ответ в Spring RestTemplate? , но мне не удалось решить эту проблему.

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

Моя цель - сделать это с помощью следующего кода:

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

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

Ответы [ 24 ]

0 голосов
/ 27 апреля 2018

Обратитесь к Q / A для регистрации запроса и ответа для шаблона отдыха, включив множественное чтение в HttpInputStream

Почему мой собственный ClientHttpRequestInterceptor с пустым ответом

0 голосов
/ 20 апреля 2018

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

Мне нужно что-то более настраиваемое, чем принятый ответ. Вот шаблон bean-компонента rest, который очень гибок и регистрирует все, как ищет 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
    }
}
0 голосов
/ 02 марта 2018

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

0 голосов
/ 19 января 2017

моя конфигурация логгера использовала 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.springframework.http.converter.json.MappingJackson2HttpMessageConverter@604525f1]

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

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

и это:

outputMessage.getBody().flush();

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

...