在项目升级到springboot3.x后,官方提供的RestClient客户端是必须体验的。
那么,在实际生产中,我们经常会遇到一种情况,就行希望在日志里打印我们对第三方接口的请求参数和返回值,以便我们更好的调参和定位问题,那么,本文就让我们来了解一下,如何使用最新的spring6 restClient打印请求和响应日志。
开始
其实,和springboot2.x时代,实现方案一样,还是通过spring的ClientHttpRequestInterceptor扩展点来实现。
ClientHttpRequestInterceptor是一个函数式接口,很简单:
ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException;
其可以拦截RestClient对外请求的request请求体,body参数,以及发生的异常,同时还能拦截返回的Response结果。
所以,实现了这个接口,我们就可以同时获得请求实际发生时的参数情况和结果值了,打印日志的功能也就有了。
show code:
打印日志的实现类
public class LoggingRequestInterceptor implements ClientHttpRequestInterceptor {
private static final Logger logger = LoggerFactory.getLogger(LoggingRequestInterceptor.class);
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
// 打印请求信息
logRequest(request, body);
// 执行请求
ClientHttpResponse response = new RepeatReadClientHttpRequestWrapper(execution.execute(request, body));
// 打印响应信息
logResponse(response);
return response;
}
private void logRequest(HttpRequest request, byte[] body) {
logger.debug("Request URI: {} {}",request.getMethod(), request.getURI());
logger.debug("Request Headers: {}", request.getHeaders());
if (body.length > 0) {
logger.debug("Request Body: {}", new String(body, StandardCharsets.UTF_8));
}
}
private void logResponse(ClientHttpResponse response) throws IOException {
logger.debug("Response Code: {} , Headers: {}", response.getStatusCode(),response.getHeaders());
StringBuilder responseBody = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(response.getBody(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
responseBody.append(line);
}
}
if (responseBody.length() > 0) {
logger.debug("Response Body: {}", responseBody.toString());
}
}
/**
* 返回的响应流只能读取一次,所以需要重新封装一个类,实现ClientHttpResponse接口,重写getBody方法,返回一个InputStream,
*/
public class RepeatReadClientHttpRequestWrapper implements ClientHttpResponse {
private ClientHttpResponse response;
private byte[] bodyData = null;
public RepeatReadClientHttpRequestWrapper(ClientHttpResponse response) {
this.response = response;
}
@Override
public HttpStatusCode getStatusCode() throws IOException {
return response.getStatusCode();
}
@Override
public String getStatusText() throws IOException {
return response.getStatusText();
}
@Override
public void close() {
response.close();
}
@Override
public InputStream getBody() throws IOException {
if (Objects.isNull(bodyData)) {
bodyData = response.getBody().readAllBytes();
}
return new ByteArrayInputStream(bodyData);
}
@Override
public HttpHeaders getHeaders() {
return response.getHeaders();
}
}
}
日志打印类实现后,还需要注入到RestClient客户端中才能实现,很简单:
还记得前文的RestClient的构建bean配置类吗?
RestClient restClient = RestClient.builder()
.baseUrl("http://localhost:8080/")
.requestInterceptor(new LoggingRequestInterceptor()) //注入日志打印类
.build();
结束,下班!
相关阅读
spring6.x使用@HttpExchange注解调用第三方接口