Open ilyas-keser opened 5 years ago
Same pb.
We have temporary created our own custom template...
But we need offical support now...
At first glance this looks a bit odd to me as well. However, I'm okay with it since I can wrap all my logic in the pipe of the parameters. I still have an issue when it comes to serialization with Jackson as I'm using REST. Whenever I try to wrap a parameter within a reactive type (Mono
/Flux
), Jackson is unable to deserialize the parameter:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class reactor.core.publisher.Mono]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `reactor.core.publisher.Mono` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: (PushbackInputStream); line: 1, column: 1]] with root cause
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `reactor.core.publisher.Mono` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: (PushbackInputStream); line: 1, column: 1]
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67) ~[jackson-databind-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1764) ~[jackson-databind-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400) ~[jackson-databind-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1209) ~[jackson-databind-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:274) ~[jackson-databind-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322) ~[jackson-databind-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4593) ~[jackson-databind-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3601) ~[jackson-databind-2.12.3.jar:2.12.3]
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:378) ~[spring-web-5.3.8.jar:5.3.8]
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:342) ~[spring-web-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:185) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:158) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:131) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121) ~[spring-web-5.3.8.jar:5.3.8]
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:170) ~[spring-web-5.3.8.jar:5.3.8]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137) ~[spring-web-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1063) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.8.jar:5.3.8]
[...]
This surely isn't the fault of the OpenAPI generator, but as it encapsulates the parameters it forces me to turn of the reactive
option and build around it to be able to still have it reactive.
For those looking for a quick workaround you may either use the async
option or the responseWrapper
option instead.
Using the async
option you can keep pretty everything as you have and convert the Mono
/Flux
afterwards to a CompletableFuture
using the toFuture()
method:
@RestController
public class MyController implements MyApi {
@Override
public CompletableFuture<ResponseEntity<MyResult>> myEndpoint(final MyDto myDto) {
return Mono.just(myDto)
.map(myService::myProcessor)
.map(myResult -> ResponseEntity.ok(myResult))
.toFuture();
}
}
When using the responseWrapper
option your controller's implementation can return any Mono
/Flux
because they implement this interface:
@RestController
public class MyController implements MyApi {
@Override
public Publisher<ResponseEntity<MyResult>> myEndpoint(final MyDto myDto) {
return Mono.just(myDto)
.map(myService::myProcessor)
.map(myResult -> ResponseEntity.ok(myResult));
}
}
I just wanted to give quick feedback that the bug still exists, a year later. When activing reactive
configOptions.set(mapOf(
"reactive" to "true",
))
Jackson cant parse requests like
override fun doSomething(
doSomethingRequest: Mono<DoSomethingRequest>?,
exchange: ServerWebExchange?
): Mono<ResponseEntity<Flux<Something>>> {
TODO()
}
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of
`reactor.core.publisher.Mono` (no Creators, like default constructor, exist): abstract types
either need to be mapped to concrete types, have custom deserializer, or contain additional
type informationat [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 1]
Is a fix realistic regarding version 7.0?
The Jackson deserialization of Mono parameters works fine for me. I am using OpenAPI generator 6.4.0. I suspect the problem some people here are facing is misconfiguration of dependencies. Most importantly, please make sure to remove the spring-boot-starter-web
dependency when turning on reactive
.
If this doesn't help, it would be great if you could share a minimal reproducible example of the problem.
At least for me this issue was fixed in the mean time. It works fine with currently generated code using the latest official version (6.6.0) and latest Spring Webflux. However, I'm using a pure reactive stack currently (excluding traditional servlet stack). To be honest I don't know what caused these issues back in the days, but the idea of @martin-mfg towards a misconfiguration seems most promising to me for now.
Does someone here have a published working CRUD example using this generated with reactive enabled? The suggested workaround above is not clear to me in how its overriding the generated Api method correctly and dealing with a reactive request parameter.
A generated controller method for Webflux (spring, reactive) look like this:
Q1: Why is myCreateModel parameter wrapped with a Mono?
Q2: Do I need ServerWebExchange param? If not, can I disable the generation?
Thx