首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >WebClient -如何获取请求正文?

WebClient -如何获取请求正文?
EN

Stack Overflow用户
提问于 2019-07-19 16:37:56
回答 3查看 6.5K关注 0票数 10

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

代码语言:javascript
复制
WebClient.builder()
    .baseUrl(properties.getEndpoint())
    .filter((request, next) -> {
        // logging
        request.body()
    })
    .build();

我可以访问url、http方法、标头,但在获取原始请求正文时遇到问题,因为请求的body()方法返回BodyInserter (BodyInserter<?, ? super ClientHttpRequest> body()

如何将请求体的BodyInserter表示转换为String表示?或者,如何正确地记录整个请求/响应,同时还能够对其中的潜在凭据进行散列?

EN

回答 3

Stack Overflow用户

发布于 2020-06-18 22:39:53

您可以围绕JSON编码器创建自己的包装器/代理类,并在序列化后的正文被发送到中间管道之前对其进行拦截。

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

具体地说,您应该扩展Jackson2JsonEncoderencodeValue方法(如果是流数据,则为encodeValues )。然后,您可以随心所欲地处理这些数据,例如日志记录等。您甚至可以根据环境/配置文件有条件地执行此操作

此自定义日志编码器可以在创建WebClient时由编解码器指定:

代码语言:javascript
复制
 CustomBodyLoggingEncoder bodyLoggingEncoder = new CustomBodyLoggingEncoder();
 WebClient.builder()
          .codecs(clientDefaultCodecsConfigurer -> {
             clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonEncoder(bodyLoggingEncoder);
             clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(new ObjectMapper(), MediaType.APPLICATION_JSON));
          })
          ...

更新2020/7/3:

这是一个匆忙的例子,应用了相同的原理,但对于解码器:

代码语言:javascript
复制
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);
        }
    }
}

您可以使用WebClient上的codecs构建器方法将其与编码器一起配置。当然,上面的方法只有在你的数据被反序列化为Mono的情况下才有效。但是,如果需要的话,可以覆盖其他方法。另外,我只是将生成的JSON输出到那里,但是您可以传入一个Consumer<String>或其他东西,让解码器将字符串发送到,或者只是从那里登录;由您决定。

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

票数 2
EN

Stack Overflow用户

发布于 2019-07-19 17:05:27

尝试设置以下属性:

代码语言:javascript
复制
logging.level.org.springframework.web.reactive.function.client.ExchangeFunctions=TRACE
logging.level.reactor.netty.http.client.HttpClient: DEBUG
spring.http.log-request-details=true
票数 0
EN

Stack Overflow用户

发布于 2022-02-03 22:35:04

BodyInserter写入ReactiveHttpOutputMessage时,请求正文可能会被访问。因此,只需创建一个FilterFunction并从现有请求中创建新请求,但是对于主体设置,new BodyInserser ()覆盖方法插入请参见下面的示例。响应和请求有效负载可以被多次读取,因为它们是在DataBuffers中缓冲的

代码语言:javascript
复制
public class TracingExchangeFilterFunction implements ExchangeFilterFunction {
 
 
        return next.exchange(buildTraceableRequest(request))
                .flatMap(response ->
                        response.body(BodyExtractors.toDataBuffers())
                                .next()
                                .doOnNext(dataBuffer -> traceResponse(response, dataBuffer))
                                .thenReturn(response)) ;
    }

    private ClientRequest buildTraceableRequest( 
            final ClientRequest clientRequest) {
        return ClientRequest.from(clientRequest).body(
                new BodyInserter<>() {
                    @Override
                    public Mono<Void> insert(
                            final ClientHttpRequest outputMessage,
                            final Context context) {
                        return clientRequest.body().insert(
                                new ClientHttpRequestDecorator(outputMessage) {
                                    @Override
                                    public Mono<Void> writeWith(final Publisher<? extends DataBuffer> body) {
                                        return super.writeWith(
                                                from(body).doOnNext(buffer ->
                                                        traceRequest(clientRequest, buffer)));
                                    }
                                }, context);
                    }
                }).build();
    }

    private void traceRequest(ClientRequest clientRequest, DataBuffer buffer) {
        final ByteBuf byteBuf = NettyDataBufferFactory.toByteBuf(buffer);
        final byte[] bytes = ByteBufUtil.getBytes(byteBuf);
        // do some tracing e.g. new String(bytes)
    }


    private void traceResponse(ClientResponse response, DataBuffer dataBuffer) {
        final byte[] bytes = new byte[dataBuffer.readableByteCount()];
        dataBuffer.read(bytes);
        // do some tracing e.g. new String(bytes)
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57108631

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档