实现ExchangeFilterFunction时如何捕获ClientRequest的请求体
捕获WebClient请求体的简化方案
直接通过读取ClientRequest的BodyInserter并重新包装请求的方式,无需额外类即可实现请求体的捕获,修改后的filter方法如下:
import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.core.io.buffer.DefaultDataBufferFactory; import org.springframework.http.client.reactive.ClientRequest; import org.springframework.http.client.reactive.ClientResponse; import org.springframework.http.client.reactive.ExchangeFilterFunction; import org.springframework.http.client.reactive.ExchangeFunction; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.web.reactive.function.BodyInserters; import reactor.core.publisher.Mono; import java.nio.charset.StandardCharsets; @Override public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) { // 读取请求体内容,处理无请求体的情况(如GET请求) Mono<String> requestBodyMono = request.body() .insert(new MockServerHttpRequest(request.method(), request.url()), new DefaultDataBufferFactory()) .map(dataBuffer -> { byte[] bytes = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(bytes); DataBufferUtils.release(dataBuffer); return new String(bytes, StandardCharsets.UTF_8); }) .defaultIfEmpty(""); return requestBodyMono.flatMap(requestBody -> { // 创建新请求,重新设置请求体(原BodyInserter已被消费,无法重复使用) ClientRequest newRequest = ClientRequest.from(request) .body(BodyInserters.fromValue(requestBody)) .build(); // 继续执行请求并处理响应日志 return next.exchange(newRequest) .flatMap(response -> response.bodyToMono(String.class) .flatMap(responseBody -> { ClientResponse clientResponse = ClientResponse.create(response.statusCode()) .headers(httpResponse -> httpResponse.addAll(response.headers().asHttpHeaders())) .body(responseBody) .build(); log.atInfo() .addKeyValue("requestHeaders", request.headers()) .addKeyValue("responseHeaders", response.headers().asHttpHeaders()) .addKeyValue("requestBody", requestBody) .addKeyValue("responseBody", parseResponseBody(responseBody)) .log(); return Mono.just(clientResponse); }) ); }); } private Object parseResponseBody(String json) { try { return objectMapper.readValue(json, Object.class); } catch (Exception e) { log.error("Failed to parse JSON", e); return json; } }
关键逻辑说明
- 读取请求体:通过
request.body().insert(...)触发BodyInserter的执行,借助MockServerHttpRequest模拟请求上下文,将请求体的DataBuffer转换为字符串。 - 重新包装请求:由于
BodyInserter只能被消费一次,读取后必须创建新的ClientRequest并重新设置请求体,确保后续请求能正常发送。 - 兼容无请求体场景:用
defaultIfEmpty("")处理GET等无请求体的请求,避免日志出现空值。
注意事项
- 字符集默认使用UTF-8,如果请求体使用其他编码,可从
request.headers().getContentType()中提取charset进行转换。 - 针对大文件等超大请求体,建议添加截断逻辑,避免内存占用过高。
内容的提问来源于stack exchange,提问作者DonutQuan
相关产品推荐
相关产品推荐

