hasan.alkhatib
hasan.alkhatib

Reputation: 1559

Spring WebFlux throws 'producer' type is unknow when I return value in the response body

I'm using Spring Boot with Kotlin, and now trying to get status value from a GET restful service by passing a handler for a reactive service.

I can see that the handler I'm passing is in the request, but whenever I'm building the body, I get this exception:

java.lang.IllegalArgumentException: 'producer' type is unknown to ReactiveAdapterRegistry
    at org.springframework.util.Assert.notNull(Assert.java:198) ~[spring-core-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException

Here is my code:

@Bean
    fun getReceiptConversionStatus() = router {
        accept(MediaType.APPLICATION_JSON).nest {
            GET("/BsGetStatus/{handler}", ::handleGetStatusRequest)
        }
    }
    private fun handleGetStatusRequest(serverRequest: ServerRequest): Mono<ServerResponse> = ServerResponse
            .ok()
            .contentType(MediaType.APPLICATION_JSON)
            .body(GetStatusViewmodel(fromObject(serverRequest.pathVariable("handler"))), GetStatusViewmodel::class.java)
            .switchIfEmpty(ServerResponse.notFound().build())

and that's my Viewmodel:

data class GetStatusViewmodel(
        @JsonProperty("handler") val documentHandler: String
)

Upvotes: 31

Views: 42115

Answers (4)

hasan.alkhatib
hasan.alkhatib

Reputation: 1559

I actually solved it, and I will post it here just in case somebody would make the same mistake I did.

I was using fromObject() method in my application "I updated the question to match my actual code". You can find this function in both of these imports, and I was using one of the overloaded body() functions to pass this wrong-placed function:

//this is the wrong import I was using
import org.springframework.web.reactive.function.server.EntityResponse.fromObject
//this is the correct one for building the mono body
import org.springframework.web.reactive.function.BodyInserters.fromObject

By using the method from BodyInserters, you will be able to pass fromObject(T) to the body method and it will return the mono result.

Upvotes: 4

Jeelan
Jeelan

Reputation: 71

The specified code resolved the issue

public Mono<ServerResponse> getName(ServerRequest request) {

    return ServerResponse.ok()
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(birthday);

}

Upvotes: 2

Ali Mizan
Ali Mizan

Reputation: 1922

For me, I was doing something like this:

webClient.post()
    .uri("/some/endpoint")
    .body(postRequestObj, PostRequest.class) // erroneous line 
    .accept(MediaType.APPLICATION_JSON)
    .retrieve()
    .bodyToMono(PostResponse.class)
    .timeout(Duration.ofMillis(5000))

When looking at the springs docs for that function body(), this is what's explained:

Variant of body(Publisher, Class) that allows using any producer that can be resolved to Publisher via ReactiveAdapterRegistry.

Parameters:
     producer - the producer to write to the request
     elementClass - the type of elements produced

Returns:
    this builder

So the first parameter can't just be any object, it has to be a producer. Changing my above code to wrap my object around in a Mono fixed this issue for me.

webClient.post()
    .uri("/some/endpoint")
    .body(Mono.just(postRequestObj), PostRequest.class)
    .accept(MediaType.APPLICATION_JSON)
    .retrieve()
    .bodyToMono(PostResponse.class)
    .timeout(Duration.ofMillis(5000))

reference: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.RequestBodySpec.html

Upvotes: 44

Toerktumlare
Toerktumlare

Reputation: 14732

Flux and Monos are Producers. They produce stuff. You are not passing in a producer in the body thats why you get the error, it doesn't recognize the producer you are passing, because you are passing in a GetStatusViewmodel.

Your body needs to be of type Mono<GetStatusViewmodel>. You can either replace body with bodyValue (it will automatically wrap it for you) or you can wrap your GetStatusViewodel in a Mono using Mono#just before passing it into the body function.

Upvotes: 36

Related Questions