如何记录封装WebClient的第三方API的请求与响应?
拦截WebClient封装的HTTP请求并记录日志
当然可以,Spring WebClient提供了原生的拦截机制ExchangeFilterFunction,不管是自研的WebClient实例,还是第三方API封装的WebClient,只要能接触到WebClient的构建器或实例,就能通过添加过滤器拦截并记录请求/响应的完整通信细节。
核心实现:自定义日志拦截过滤器
通过ExchangeFilterFunction可以分别拦截请求发送前和响应返回后的处理逻辑,示例代码如下:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.reactive.function.client.ExchangeFilterFunction; import reactor.core.publisher.Mono; public class WebClientLoggingFilter { private static final Logger logger = LoggerFactory.getLogger(WebClientLoggingFilter.class); public static ExchangeFilterFunction logRequestResponse() { // 拦截并记录请求 ExchangeFilterFunction requestFilter = ExchangeFilterFunction.ofRequestProcessor(clientRequest -> { logger.info("Request: {} {}", clientRequest.method(), clientRequest.url()); // 打印请求头(生产环境可调整为DEBUG级别避免冗余) clientRequest.headers().forEach((headerName, values) -> values.forEach(value -> logger.debug("Request Header: {} = {}", headerName, value))); return Mono.just(clientRequest); }); // 拦截并记录响应 ExchangeFilterFunction responseFilter = ExchangeFilterFunction.ofResponseProcessor(clientResponse -> { logger.info("Response: {} {}", clientResponse.statusCode(), clientResponse.request().url()); // 打印响应头 clientResponse.headers().asHttpHeaders().forEach((headerName, values) -> values.forEach(value -> logger.debug("Response Header: {} = {}", headerName, value))); // 必须缓存响应体,避免下游无法读取原始响应 return clientResponse.cache(); }); return requestFilter.andThen(responseFilter); } }
不同场景下的添加方式
- 自研WebClient实例
创建WebClient时直接添加过滤器:
WebClient webClient = WebClient.builder() .baseUrl("https://api.example.com") .filter(WebClientLoggingFilter.logRequestResponse()) .build();
- 第三方封装的WebClient Bean
如果第三方将WebClient作为Spring Bean暴露,可在配置类中修改Bean定义:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.reactive.function.client.WebClient; @Configuration public class WebClientConfig { @Bean public WebClient thirdPartyWebClient(WebClient.Builder webClientBuilder) { return webClientBuilder // 假设第三方的WebClient原本有其他配置,这里追加日志过滤器 .filter(WebClientLoggingFilter.logRequestResponse()) .build(); } }
- 全局拦截所有WebClient实例
如果需要拦截所有通过WebClient.Builder创建的WebClient(包括第三方内部构建的),使用WebClientCustomizer实现全局配置:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.reactive.function.client.WebClientCustomizer; @Configuration public class GlobalWebClientLoggingConfig { @Bean public WebClientCustomizer webClientLoggingCustomizer() { return builder -> builder.filter(WebClientLoggingFilter.logRequestResponse()); } }
注意事项
- 记录响应体时,必须调用
clientResponse.cache(),否则响应体被日志逻辑消费后,下游业务代码将无法读取响应内容。如果需要打印响应体内容,可通过clientResponse.bodyToMono(String.class)读取后再重新封装响应。 - 生产环境需注意日志级别,避免敏感信息(如Authorization头、请求体中的密码)被泄露,建议对敏感字段做脱敏处理。
- 如果第三方封装的WebClient完全隐藏了实例和构建器,无法通过上述方式添加过滤器,可考虑用AOP拦截其请求方法,但这种方式复杂度较高,优先推荐使用
ExchangeFilterFunction。
内容的提问来源于stack exchange,提问作者StSch
相关产品推荐
相关产品推荐

