繁体   English   中英

WebClient 请求和响应正文记录

[英]WebClient Request and Response body logging

我正在尝试根据发出 WebClient 调用时收到的请求和响应数据制作 POJO。 但是我没有以字符串/JSON 可读形式获取请求正文,而是获取了 BodyInsertor。 我正在使用 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;
    };
  }

我关注了一些文章,例如https://andrew-flower.com/blog/webclient-body-logginghttps://www.gitmemory.com/issue/spring-projects/spring-framework/24262/570245788但没有任何效果为了我。

我的最终目标是用他们的身体捕捉请求和响应,并生成为 Kafka 收集的数据。

在 ExchangeFilterFunction 中,您可以访问 HTTP 方法、URL、标头、cookies,但不能直接从此过滤器访问请求或响应正文。

请参阅此处的答案。 它提供了一种访问请求和响应正文的方法。 它还提供了指向此博客文章的链接。 它解释了如何在 Web 客户端中获取 JSON/String 格式的正文。

您可以通过对请求和响应的小操作来跟踪请求和响应有效负载:

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

要添加到 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);
    };
}

但是要记录响应正文,您必须将 go 到 ClientHttpResponse 的较低级别,然后允许您拦截正文。 首先像这样扩展 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)));
}

}

然后像这样创建 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);
}

}

最后在构建 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