如何记录Spring 5 WebClient的REST请求与响应(含POST调用)?
嘿,作为刚接触Spring WebClient的新手,能理解你想把第三方REST服务的请求和响应完整记录下来的需求——毕竟调试接口、排查问题的时候,这些日志简直是救命神器!下面我给你分享几种最实用的实现方式,重点针对POST请求的场景:
方法一:自定义ExchangeFilterFunction(官方推荐的灵活方案)
WebClient自带的ExchangeFilterFunction是实现请求/响应日志最常用的方式,它能拦截请求发送前和响应返回后的所有细节,完全支持自定义日志格式。
步骤1:编写日志过滤器
你需要实现两个核心逻辑:记录请求(包括POST的请求体)、记录响应(包括响应体)。注意请求体和响应体都是一次性消费的数据流,直接读取后要缓存起来,避免后续流程无法获取数据。
import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.client.reactive.ClientHttpRequest; import org.springframework.http.client.reactive.ClientHttpResponse; import org.springframework.web.reactive.function.client.ExchangeFilterFunction; import reactor.core.publisher.Mono; import java.nio.charset.StandardCharsets; import java.util.function.Predicate; public class WebClientLoggingFilter { // 判断是否是可打印的文本类型(避免日志输出二进制数据) private static final Predicate<MediaType> IS_TEXT_TYPE = mediaType -> mediaType != null && (mediaType.isCompatibleWith(MediaType.APPLICATION_JSON) || mediaType.isCompatibleWith(MediaType.TEXT_PLAIN) || mediaType.isCompatibleWith(MediaType.APPLICATION_FORM_URLENCODED)); // 记录请求日志 public static ExchangeFilterFunction logRequest() { return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> { StringBuilder sb = new StringBuilder(); sb.append("Sending request: ").append(clientRequest.method()).append(" ").append(clientRequest.url()); // 记录请求头 HttpHeaders headers = clientRequest.headers(); if (!headers.isEmpty()) { sb.append("\nHeaders: ").append(headers); } // 记录POST/PUT请求体(仅文本类型) if (HttpMethod.POST.equals(clientRequest.method()) || HttpMethod.PUT.equals(clientRequest.method())) { clientRequest.body().map(dataBuffer -> { byte[] bytes = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(bytes); DataBufferUtils.release(dataBuffer); String body = new String(bytes, StandardCharsets.UTF_8); if (IS_TEXT_TYPE.test(clientRequest.headers().getContentType())) { sb.append("\nRequest body: ").append(body); } return bytes; }).subscribe(); } // 这里替换成你项目的日志框架,比如SLF4J的log.debug System.out.println(sb.toString()); return Mono.just(clientRequest); }); } // 记录响应日志 public static ExchangeFilterFunction logResponse() { return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> { StringBuilder sb = new StringBuilder(); sb.append("Received response: ").append(clientResponse.statusCode()); // 记录响应头 HttpHeaders headers = clientResponse.headers().asHttpHeaders(); if (!headers.isEmpty()) { sb.append("\nHeaders: ").append(headers); } // 记录响应体(仅文本类型) return clientResponse.body((dataBuffer, context) -> { byte[] bytes = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(bytes); DataBufferUtils.release(dataBuffer); String body = new String(bytes, StandardCharsets.UTF_8); if (IS_TEXT_TYPE.test(clientResponse.headers().contentType())) { sb.append("\nResponse body: ").append(body); } // 这里替换成你项目的日志框架 System.out.println(sb.toString()); return Mono.just(context.bufferFactory().wrap(bytes)); }); }); } }
步骤2:将过滤器添加到WebClient
创建WebClient实例的时候,把上面的两个过滤器整合进去:
import org.springframework.web.reactive.function.client.WebClient; public class WebClientConfig { public WebClient createWebClient() { return WebClient.builder() .baseUrl("https://第三方服务基础地址") .filter(WebClientLoggingFilter.logRequest()) .filter(WebClientLoggingFilter.logResponse()) .build(); } }
方法二:利用Spring Boot配置(快速开启基础日志)
如果你只需要基础的请求/响应元数据(比如URL、状态码、头信息),不想写自定义代码,可以直接在application.properties里加配置:
# 开启WebClient请求详情日志 spring.webclient.log-request-details=true
然后把日志级别调到DEBUG(比如在logback.xml里配置org.springframework.web.reactive.function.client的级别为DEBUG),就能看到框架自动输出的基础日志了。不过这种方式没法直接记录请求体和响应体,适合快速排查简单问题。
关键注意事项
- 避免重复消费数据流:请求体和响应体都是
DataBuffer类型,读取后必须释放并重新包装,否则后续的业务逻辑会因为数据流已被消费而报错,上面的示例代码已经处理了这个问题。 - 过滤敏感信息:如果请求/响应里有密码、token等敏感数据,一定要在日志里过滤掉,比如替换成
***,避免泄露。 - 日志级别控制:日志量较大的时候,建议把日志级别设为
DEBUG或TRACE,生产环境可以根据需求调整,避免影响性能。
这样配置后,你的POST请求的URL、请求头、请求体,还有响应的状态码、响应头、响应体都会被完整记录下来啦!
内容的提问来源于stack exchange,提问作者ddzz
相关产品推荐
相关产品推荐

