我已经使用Spring RestTemplate有一段时间了,当我试图调试它的请求和响应时,我总是碰壁。我基本上希望看到与打开“verbose”选项时使用curl时相同的东西。例如:
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问题是行不通的(在某些情况下)。
您可以使用spring-rest-template-logger来记录RestTemplate HTTP流量。
在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()
确保在application.properties中启用了调试日志记录:
logging.level.org.hobsoft.spring.resttemplatelogger.LoggingCustomizer = DEBUG
现在所有的RestTemplate HTTP流量将被记录到调试级别的org.hobsoft.spring.resttemplatelogger.LoggingCustomizer。
免责声明:这个库是我写的。
在Apache HttpClient的帮助下登录Logback:
在类路径中需要Apache HttpClient:
<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"/>
这些答案都不能完全解决问题。Mjj1409实现了大部分功能,但方便地避免了记录响应的问题,因为这需要更多的工作。Paul Sabou提供了一个看似现实的解决方案,但没有提供足够的细节来实际实现(对我来说根本没用)。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);
wire提供了太多不可读的日志,所以我使用日志记录应用程序Servlet和RestTemplate请求和响应的有效负载。
build.gradle:
compile group: 'org.zalando', name: 'logbook-spring-boot-starter', version: '2.6.2'
或Maven依赖:
<dependency>
<groupId>org.zalando</groupId>
<artifactId>logbook-spring-boot-starter</artifactId>
<version>2.6.2</version>
</dependency>
应用程序。属性(或槽YAML):
logging.level.org.zalando.logbook = TRACE
RestTemplate.java:
import java.util.function.Supplier;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import org.zalando.logbook.httpclient.LogbookHttpRequestInterceptor;
import org.zalando.logbook.httpclient.LogbookHttpResponseInterceptor;
@Configuration
public class RestTemplateConfiguration {
private final LogbookHttpRequestInterceptor logbookHttpRequestInterceptor;
private final LogbookHttpResponseInterceptor logbookHttpResponseInterceptor;
public RestTemplateConfiguration(LogbookHttpRequestInterceptor logbookHttpRequestInterceptor,
LogbookHttpResponseInterceptor logbookHttpResponseInterceptor) {
this.logbookHttpRequestInterceptor = logbookHttpRequestInterceptor;
this.logbookHttpResponseInterceptor = logbookHttpResponseInterceptor;
}
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder
.requestFactory(new MyRequestFactorySupplier())
.build();
}
class MyRequestFactorySupplier implements Supplier<ClientHttpRequestFactory> {
@Override
public ClientHttpRequestFactory get() {
// Using Apache HTTP client
CloseableHttpClient client = HttpClientBuilder.create()
.addInterceptorFirst(logbookHttpRequestInterceptor)
.addInterceptorFirst(logbookHttpResponseInterceptor)
.build();
return new HttpComponentsClientHttpRequestFactory(client);
}
}
}