membersound
membersound

Reputation: 86687

ExchangeFilterFunction executing code outside WebClient reactive flow?

I'm creating ExchangeFilterFunction for extended logging during WebClient requests.

Question: is there any difference if I execute the logging inside the .doOnNext() function in the following example? Or are they equal in processing?

ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
    LOGGER.info("Request url=" + clientResponse.url() + ", method=" + clientResponse.method());
    return Mono.just(clientRequest);
});

ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
    return Mono.just(clientRequest).doOnNext(clientResponse2 -> {
        LOGGER.info("Request url=" + clientResponse2.url() + ", method=" + clientResponse2.method());
    });
});

Is there any advantage for one over the other?

Upvotes: 2

Views: 2443

Answers (2)

K.Nicholas
K.Nicholas

Reputation: 11551

I just loath any artificial introduction of Mono.just or similar into your flow. I suppose that .doOnNext is one extra step but I cannot imagine it makes any difference whatsoever. Better to avoid breaking the flow up. I think that would be your worst performance penalty.

Note: This will log when WebClient is invoked, as noted below, and generally looks a little uglier than using the convenience methods, but it avoids using Mono.just.

@Override
public void run(ApplicationArguments args) throws Exception {
    WebClient webClient = WebClient.builder()
            .baseUrl("http://localhost:8080/")
            .filter(eff)
            .build();
    webClient.get().retrieve().bodyToMono(String.class).subscribe(System.out::println);
}

ExchangeFilterFunction eff = (cr,ef)->{
    System.out.println("method: " + cr.method());
    return ef.exchange(cr)
    .map(cresp->{
        System.out.println(cresp.rawStatusCode());
        return cresp;
    });
};

Upvotes: 2

Adam Bickford
Adam Bickford

Reputation: 1386

In this particular case the webclient will invoke the filter correctly. However, be careful about this in general.

In the first case the logging will happen when the method is invoked. It could actually be incorrect if the caller never subscribes to the returned publisher. The publisher could also be called many times like in the case of a retry or repeat, etc. and that will be wrong too.

In the second case it will be called every time it is subscribed to.

public class Foo {

    static Mono<String> echo(String message) {
        System.out.println("echo called with " + message);
        return Mono.just(message);
    }

    static Mono<String> rxEcho(String message) {
        return Mono.just(message).doOnNext(msg -> System.out.println("rxEcho was called with " + msg));
    }

    public static void main(String[] args) {
        //logged, even though never actually called
        echo("no sub");
        //only logs 1 time
        echo("retry")
            .repeat(3)
            .blockLast();

        System.out.println("---------------");

        //never subbed, never logged
        rxEcho("no sub");

        //all 4 calls are logged
        rxEcho("retry")
            .repeat(3)
            .blockLast();
    }
}

Also note you shouldn't call any flavor of block() in real code, I didn't want to confuse the example with StepVerifier.

Upvotes: 0

Related Questions