Home > OS >  Spring Webflux - "scanAvailable": true
Spring Webflux - "scanAvailable": true

Time:04-30

I'm using a RouterFunction to define endpoints in my Spring Boot application. My service returns a Mono<Object> and I want to return the result of this when the endpoint is called. I also need to authenticate so I pass a UserPrinciple object through.

Router

@Bean
RouterFunction<ServerResponse> router() {
    return route()
            .GET("/api/endpoint-name", this::getExample)
            .build();
}

private Mono<ServerResponse> getExample(ServerRequest request) {
    return ServerResponse.ok().body(fromPublisher(getUserPrincipal().map(service::getSomething), Object.class)).log();
}

private Mono<UserPrincipal> getUserPrincipal() {
    return ReactiveSecurityContextHolder.getContext()
            .map(ctx -> ctx.getAuthentication())
            .map(auth -> auth.getPrincipal())
            .map(UserPrincipal.class::cast);
}

Service

public Mono<Object> getSomething(UserPrincipal userPrincipal) {
    WebClient webClient = getWebClient(userPrincipal.getJwt());

    return webClient.get()
            .uri(uriBuilder -> uriBuilder.path("another/server/endpoint").build())
            .retrieve()
            .bodyToMono(Object.class);
}

The endpoint is returning this:

{
    "scanAvailable": true
}

which suggests that I'm passing the Mono into the body of the response instead of passing in the result. However I've used fromPublisher which I thought would resolve this.

I can't find any examples where the service returns a Mono and the route correctly returns the result of the Mono.

How can I correctly pass a Mono/Flux as the body of the response?

CodePudding user response:

im not going to explain the difference between mapand flatMapsince i have already written a quite comprehensive explanation here:

Do you have a test to show differences between the reactor map() and flatMap()?

The problem in the above code is the return of Object. And input parameters of Object into certain functions. The first function is pretty straight forward

Mono<UserPrincipal> = getUserPrincipal();

While the second one gets a bit more hairy:

Mono<Mono<Object> value = getUserPrincipal().map(service::getSomething);

So why are we getting A nested Mono?, well the get something returns a Mono<Object> and the Map return according the the api is Mono<R> where R is what we return from getSomething.

We then stick it into the fromPublisher which will unrap the first Mono ending up trying to serialize the Mono<Object>resulting in the strange response.

{
    "scanAvailable": true
}

The answer here is pay more close attention to the type system. The body function takes a Publisher (Mono or Flux) so you don't need the fromPublisher function.

And also changing map to flatMap since the return type from inside a flatMap is a publisher.

ServerResponse.ok()
    .body(getUserPrincipal()
    .flatMap(service::getSomething), Object.class));
  • Related