简体   繁体   English

WebClient 请求和响应正文记录

[英]WebClient Request and Response body logging

I am trying to make a POJO out of request and response data received when making WebClient calls.我正在尝试根据发出 WebClient 调用时收到的请求和响应数据制作 POJO。 But I am not getting the request body in string/JSON readable form instead I am getting a BodyInsertor.但是我没有以字符串/JSON 可读形式获取请求正文,而是获取了 BodyInsertor。 I am making use of Exchange Filters.我正在使用 Exchange 过滤器。

public ExchangeFilterFunction logWebRequest() {
    return (request, next) -> {
      log.info("Entered in logWebRequest for WebClient");
      long startTime = System.currentTimeMillis();
      Mono<ClientResponse> response = next.exchange(request);
      long processingTimeInMs = System.currentTimeMillis() - startTime;

      // request.body() -> Gives Body Insertor

      WebRequestLog webRequestLog = webRequestService.makeWebRequestLog(request, response.block());
      webRequestLog.setProcessingTimeInMs(processingTimeInMs);

      log.info("WebRequest to be produced to kafka topic: " + webRequestLog);
      kafkaService.produceAuditLog(webRequestLog);
      return response;
    };
  }

I followed some articles such as https://andrew-flower.com/blog/webclient-body-logging and https://www.gitmemory.com/issue/spring-projects/spring-framework/24262/570245788 but nothing worked for me.我关注了一些文章,例如https://andrew-flower.com/blog/webclient-body-logginghttps://www.gitmemory.com/issue/spring-projects/spring-framework/24262/570245788但没有任何效果为了我。

My end goal is to capture requests and responses with their bodies and produce the data collected for Kafka.我的最终目标是用他们的身体捕捉请求和响应,并生成为 Kafka 收集的数据。

Inside ExchangeFilterFunction, you can access HTTP method, URL, headers, cookies but request or response body can not be accessed directly from this filter.在 ExchangeFilterFunction 中,您可以访问 HTTP 方法、URL、标头、cookies,但不能直接从此过滤器访问请求或响应正文。

Refer to the answer here .请参阅此处的答案。 It provides a way to get access to the request and response body.它提供了一种访问请求和响应正文的方法。 It also provides a link to This blog post .它还提供了指向此博客文章的链接。 It explains how to get the body in JSON/String format in Web Client.它解释了如何在 Web 客户端中获取 JSON/String 格式的正文。

You can do tracing of request and response payloads with small manipulations with request and responses:您可以通过对请求和响应的小操作来跟踪请求和响应有效负载:

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)
    }
}

To add to Vicky Ajmera answer best way to get and log a request is with ExchangeFilterFunction.要添加到 Vicky Ajmera ,获取和记录请求的最佳方法是使用 ExchangeFilterFunction。

private ExchangeFilterFunction logRequest() {
    return (clientRequest, next) -> {
        logger.info("Request: {} {} {}", clientRequest.method(), clientRequest.url(), clientRequest.body());
        clientRequest.headers()
                .forEach((name, values) -> values.forEach(value -> logger.info("{}={}", name, value)));
        return next.exchange(clientRequest);
    };
}

But to log a response body you will have to go to the lower level of ClientHttpResponse which then allows you to intercept the body.但是要记录响应正文,您必须将 go 到 ClientHttpResponse 的较低级别,然后允许您拦截正文。 First extend ClientHttpResponseDecorator like this:首先像这样扩展 ClientHttpResponseDecorator:

public class LoggingClientHttpResponse extends ClientHttpResponseDecorator {

private static final Logger logger = LoggerFactory.getLogger(LoggingClientHttpResponse.class);
private static final DataBufferFactory bufferFactory = new DefaultDataBufferFactory();
private final DataBuffer buffer = bufferFactory.allocateBuffer();


public LoggingClientHttpResponse(ClientHttpResponse delegate) {
    super(delegate);
}

@Override
public Flux<DataBuffer> getBody() {
    return super.getBody()
            .doOnNext(this.buffer::write)
            .doOnComplete(() -> logger.info("Response Body: {}", buffer.toString(StandardCharsets.UTF_8)));
}

} }

Then create your implementation of ClientHttpConnector like this:然后像这样创建 ClientHttpConnector 的实现:

public class LoggingClientHttpConnector implements ClientHttpConnector {
private final ClientHttpConnector delegate;

public LoggingClientHttpConnector(ClientHttpConnector delegate) {
    this.delegate = delegate;
}

@Override
public Mono<ClientHttpResponse> connect(HttpMethod method, URI uri, Function<? super ClientHttpRequest, Mono<Void>> requestCallback) {
    return this.delegate.connect(method, uri, requestCallback).map(LoggingClientHttpResponse::new);
}

} }

And last when building your WebClient add a connector:最后在构建 WebClient 时添加一个连接器:

    HttpClient httpClient = HttpClient.create();
    ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);

    WebClient.builder()
            .baseUrl("http://localhost:8080")
            .clientConnector(new LoggingClientHttpConnectorDecorator(connector))
            .filter(logRequest())
            .build();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM