我已经使用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问题是行不通的(在某些情况下)。
用一些代码扩展@hstoerr answer:
创建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());
您可以使用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。
免责声明:这个库是我写的。
这些答案都不能完全解决问题。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);
---- 2019年7月----
(使用Spring Boot)
让我感到惊讶的是,Spring Boot拥有所有的零配置魔法,却没有提供一种使用RestTemplate检查或记录简单JSON响应体的简单方法。我浏览了这里提供的各种答案和评论,并分享了我自己的(仍然)有效的版本,并且在我看来是一个合理的解决方案,考虑到当前的选项(我使用Spring Boot 2.1.6和Gradle 4.4)
1. 使用Fiddler作为http代理
这实际上是一个相当优雅的解决方案,因为它绕过了创建自己的拦截器或将底层http客户端更改为apache的所有繁琐工作(见下文)。
安装并运行Fiddler
然后
add -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作为RestTemplate的RequestFactory。最简单的方法是:
RestTemplate = new RestTemplate();
创建restTemplate。setRequestFactory(新HttpComponentsClientHttpRequestFactory ());
在应用程序中启用DEBUG。属性文件(如果您使用Spring Boot)
logging.level.org.apache.http =调试
如果你正在使用Spring Boot,你需要确保你有一个日志框架的设置,例如通过使用一个Spring - Boot -starter依赖项,其中包括Spring - Boot -starter-logging。
3.使用拦截器
我会让你通读提案、反提案以及其他答案和评论中的陷阱,然后你自己决定是否要走这条路。
4. 无正文记录URL和响应状态
尽管这并不满足记录主体的要求,但这是开始记录REST调用的一种快速而简单的方法。它显示完整的URL和响应状态。
只需将以下行添加到应用程序中。properties文件(假设您正在使用Spring Boot,并且假设您正在使用包含Spring - Boot -starter-logging的Spring Boot启动器依赖项)
logging.level.org.springframework.web.client.RestTemplate =调试
输出看起来像这样:
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]
下面是我用来在RestTemplate中记录整个http请求/响应而不丢失响应体信息的解决方案。Spring引导版本为<version>2.7.5</version>
1.创建LoggingInterceptor类
@Component
@Slf4j
public class LoggingInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
traceRequest(request, body);
ClientHttpResponse response = execution.execute(request, body);
response = traceResponse(response);
return response;
}
private void traceRequest(HttpRequest request, byte[] body) throws IOException {
if (!log.isDebugEnabled()) {
return;
}
log.debug("=========================== Request Begin ===========================");
log.debug("URI : " + request.getURI());
log.debug("Method : " + request.getMethod());
log.debug("Headers : " + request.getHeaders());
log.debug("Body : " + new String(body, "utf-8"));
log.debug("============================ Request End ============================");
}
private ClientHttpResponse traceResponse(ClientHttpResponse response) throws IOException {
if (!log.isDebugEnabled()) {
return response;
}
ClientHttpResponse newCopiedResponse = new BufferingClientHttpResponseWrapper(response);
StringBuilder inputStringBuilder = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(newCopiedResponse.getBody(), "UTF-8"));
String line = bufferedReader.readLine();
while (line != null) {
inputStringBuilder.append(line);
line = bufferedReader.readLine();
}
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 ============================");
return newCopiedResponse;
}
/**
* Wrapper around ClientHttpResponse, buffers the body so it can be read repeatedly (for logging & consuming the result).
*/
private static class BufferingClientHttpResponseWrapper implements ClientHttpResponse {
private final ClientHttpResponse response;
private byte[] body;
public BufferingClientHttpResponseWrapper(ClientHttpResponse response) {
this.response = response;
}
@Override
public InputStream getBody() throws IOException {
if (body == null) {
body = StreamUtils.copyToByteArray(response.getBody());
}
return new ByteArrayInputStream(body);
}
@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 HttpHeaders getHeaders() {
return this.response.getHeaders();
}
@Override
public void close() {
this.response.close();
}
}
}
2.将其附加到RestTemplate bean
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate createRestTemplate(LoggingInterceptor loggingInterceptor) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setInterceptors(Collections.singletonList(loggingInterceptor));
return restTemplate;
}
}
3.在应用程序中应用适当的级别
logging:
level:
com:
test: DEBUG
在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"/>