WebClient-如何获取请求正文?

时间:2019-07-19 08:37:55

标签: spring spring-webflux

我已经开始使用WebClient,并且正在添加请求/响应的日志记录,并且在构造WebClient时正在使用过滤器方法:

WebClient.builder()
    .baseUrl(properties.getEndpoint())
    .filter((request, next) -> {
        // logging
        request.body()
    })
    .build();

我可以访问url,http方法,标头,但是我无法获取原始请求主体,因为请求的主体是body()请求的方法返回了BodyInserterBodyInserter<?, ? super ClientHttpRequest> body()

如何将BodyInserter转换为请求正文的String表示形式?另外,如何在正确记录整个请求/响应的同时还能够对其中的潜在凭据进行哈希处理?

2 个答案:

答案 0 :(得分:1)

您可以围绕JSON编码器创建自己的wrapper / proxy类,并在将序列化的正文发送到插管之前对其进行拦截。

blog post显示了如何记录WebClient请求和响应的JSON有效负载

具体来说,您将扩展encodeValue的{​​{1}}方法(或在流数据的情况下为encodeValues)。然后,您可以根据需要处理这些数据,例如日志记录等。甚至可以根据环境/配置文件有条件地进行处理

可以在创建Jackson2JsonEncoder时通过编解码器指定此自定义日志记录编码器:

WebClient

更新2020/7/3:

这是一个应用相同原理但仅适用于解码器的匆忙示例:

 CustomBodyLoggingEncoder bodyLoggingEncoder = new CustomBodyLoggingEncoder();
 WebClient.builder()
          .codecs(clientDefaultCodecsConfigurer -> {
             clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonEncoder(bodyLoggingEncoder);
             clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(new ObjectMapper(), MediaType.APPLICATION_JSON));
          })
          ...

您将使用public class LoggingJsonDecoder extends Jackson2JsonDecoder { private final Consumer<byte[]> payloadConsumer; public LoggingJsonEncoder(final Consumer<byte[]> payloadConsumer) { this.payloadConsumer = payloadConsumer; } @Override public Mono<Object> decodeToMono(final Publisher<DataBuffer> input, final ResolvableType elementType, final MimeType mimeType, final Map<String, Object> hints) { // Buffer for bytes from each published DataBuffer final ByteArrayOutputStream payload = new ByteArrayOutputStream(); // Augment the Flux, and intercept each group of bytes buffered final Flux<DataBuffer> interceptor = Flux.from(input) .doOnNext(buffer -> bufferBytes(payload, buffer)) .doOnComplete(() -> payloadConsumer.accept(payload.toByteArray())); // Return the original method, giving our augmented Publisher return super.decodeToMono(interceptor, elementType, mimeType, hints); } private void bufferBytes(final ByteArrayOutputStream bao, final DataBuffer buffer) { try { bao.write(ByteUtils.extractBytesAndReset(buffer)); } catch (IOException e) { throw new RuntimeException(e); } } } 上的codecs构建器方法将其与编码器一起配置。 当然,以上假设只适用于将您的数据反序列化为Mono的情况。但是,如果需要,请覆盖其他方法。另外,我只是在这里输出结果JSON,但是您可以传入WebClient或其他东西,以使解码器将字符串发送到,例如,或者只是从那里登录;由你决定。

警告一句,以目前的形式,这将使您的内存使用量增加一倍,因为它会缓冲整个响应。如果您可以将该字节数据发送到另一个进程/线程以立即写入日志文件或某些输出流(甚至Flux),则可以避免将整个有效负载缓冲在内存中。

答案 1 :(得分:0)

尝试设置以下属性:

logging.level.org.springframework.web.reactive.function.client.ExchangeFunctions=TRACE
logging.level.reactor.netty.http.client.HttpClient: DEBUG
spring.http.log-request-details=true