0

i use spring webClient and have a code like this :

public String HttpCall() {
 
    Insurer insurer = new Insurer();
    insurer.setName(“Axa”);
    insurer.setId(“T-185785”);
 
    ClientResponse response = webclient.post()
        .uri("http://localhost:8080/process/insurer")
        .header(HttpHeaders.AUTHORIZATION, token)
        .accept(MediaType.APPLICATION_JSON)
        .bodyValue(insurer)
        .exchange()
        .block(Duration.ofMinutes(5))
   
    if (response.statusCode() == HttpStatus.OK) {
       return response.bodyToMono(String.class).block();
    } else
      // eventually, other code here;
    }
}

What i need is to display on console the payload as a pretty well formated json of my body (in my codes snippet : insurer pojo)

Does anybody has any idea ?

Best regards.

Toerktumlare
  • 12,548
  • 3
  • 35
  • 54
electrode
  • 205
  • 1
  • 4
  • 16

3 Answers3

0

There are multiple ways to pretty print a JSON. Assuming it is for non production and more from debugging.

I have shared an example using jackson (you can use any library you like, also can use a logger of your choice, Used System.out.print only to demonstrate.

You need to add a dependency.

 <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.0</version>
</dependency>

A simple class to demonstrate how it can be done is shown below.

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import reactor.core.publisher.Mono;

public class MonoImpl {

    private static void testPrintInMono() throws JsonProcessingException {
        String test="{}";
       String atr = Mono.just(test).doOnNext(s->{
       JsonNode a = null;
       try {
            a = new ObjectMapper().readValue(s, JsonNode.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        a.toPrettyString();
        System.out.println(a.toPrettyString());
    }).block();

}

  private static void testPrintAfterUnBlock() throws JsonProcessingException {
      String test="{}";
      String atr = Mono.just(test).block();
      JsonNode a = new ObjectMapper().readValue(atr, JsonNode.class);
      a.toPrettyString();
      System.out.println(a.toPrettyString());
   }

  public static void main(String[] args) throws JsonProcessingException {
    testPrintAfterUnBlock();
    testPrintInMono();

   }
}
pradosh nair
  • 913
  • 1
  • 16
  • 28
0

Edit: Note that this implementation is not thread-safe. It doesn't currently work for concurrent requests

This can be done by implementing your extension of the Jackson2JsonEncoder that logs the request body.

This is explained in this blog post: https://andrew-flower.com/blog/webclient-body-logging (which provides a thread-safe version).

When building your WebClient, you can specify the custom encoder:

WebClient webClient = WebClient.builder()
                               .baseUrl(...)
                               .codecs(codecConfigurer -> {
                                    codecConfigurer.defaultCodecs().jackson2JsonEncoder(loggingEncoder);
                               })
                               ...
                               .build();

An example of an encoder that produces the serialized bytes is this:

public class LoggingJsonEncoder extends Jackson2JsonEncoder {
    private final Consumer<byte[]> payloadConsumer;

    public LoggingJsonEncoder(final Consumer<byte[]> payloadConsumer) {
        this.payloadConsumer = payloadConsumer;
    }

    @Override
    public DataBuffer encodeValue(final Object value, final DataBufferFactory bufferFactory,
                                  final ResolvableType valueType, @Nullable final MimeType mimeType, @Nullable final Map<String, Object> hints) {

        // Encode/Serialize data to JSON
        final DataBuffer data = super.encodeValue(value, bufferFactory, valueType, mimeType, hints);

        // Interception: Generate Signature and inject header into request
        payloadConsumer.accept(extractBytesAndReset(data));

        // Return the data as normal
        return data;
    }

    private byte[] extractBytesAndReset(final DataBuffer data) {
        final byte[] bytes = new byte[data.readableByteCount()];
        data.read(bytes);
        data.readPosition(0);
        return bytes;
    }
}

So you could create the encoder with a consumer that prints to Stdout, and pass it to the WebClient builder in the first snippet.

LoggingJsonEncoder loggingEncoder = new LoggingJsonEncoder(bytes -> System.out.print(new String(bytes)));

Bear in mind that this will be slow because it is synchronously reading the whole serialized data. So use this only in dev/debug mode.

rewolf
  • 5,561
  • 4
  • 40
  • 51
  • it perfectly works! just made a mistake : bodyConsumer should be replaced by payloadConsumer (to execute accept method). – electrode Feb 14 '21 at 11:24
0

You can achieve response and request payload tracing using filter function and small manipulations with request decorator:

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
}


private void traceResponse(ClientResponse response, DataBuffer dataBuffer) {
    final byte[] bytes = new byte[dataBuffer.readableByteCount()];
    dataBuffer.read(bytes);
    // do some tracing
}

}

Oleg Maksymuk
  • 441
  • 4
  • 10