ServerWebExchange cannot be instantiated when implementing reactive code generated with OpenAPI Generator

Viewed 79

I decided to try out API First approach with Spring-webflux. I sorted out the code generation part, however when I call one of the generated endpoints I get the following error:

2022-08-05 14:23:41.257 ERROR 22404 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: No primary or single unique constructor found for interface org.springframework.web.server.ServerWebExchange] with root cause

java.lang.IllegalStateException: No primary or single unique constructor found for interface org.springframework.web.server.ServerWebExchange
    at org.springframework.beans.BeanUtils.getResolvableConstructor(BeanUtils.java:267) ~[spring-beans-5.3.21.jar:5.3.21]
...

I am using https://petstore3.swagger.io/ as a starting point till I get the code-gen part configured.


The POM has the following plugin configuration (along with all of the required dependencies):

<plugin>
    <groupId>org.openapitools</groupId>
    <artifactId>openapi-generator-maven-plugin</artifactId>
    <version>6.0.1</version>
    <executions>
        <execution>
            <goals>
                <goal>generate</goal>
            </goals>
            <configuration>
                <inputSpec>${project.basedir}/src/main/resources/api/petstore.yml</inputSpec>
                <generatorName>spring</generatorName>
                <output>${project.build.directory}/generated-sources/swagger</output>
                <modelNameSuffix>DTO</modelNameSuffix>
                <generateSupportingFiles>true</generateSupportingFiles>
                <supportingFilesToGenerate>
                                ApiUtil.java
                            </supportingFilesToGenerate>
                <configOptions>
                    <reactive>true</reactive>
                    <delegatePattern>false</delegatePattern>
                    <library>spring-boot</library>
                    <dateLibrary>java8</dateLibrary>
                    <interfaceOnly>true</interfaceOnly>
                    <performBeanValidation>true</performBeanValidation>
                    <useOptional>true</useOptional>
                </configOptions>
            </configuration>
        </execution>
    </executions>
</plugin>

This resulted in the following generated code:

@RequestMapping(
        method = RequestMethod.GET,
        value = "/pet/{petId}",
        produces = { "application/json", "application/xml" }
    )
    default Mono<ResponseEntity<PetDTO>> getPetById(
        @Parameter(name = "petId", description = "ID of pet to return", required = true) @PathVariable("petId") Long petId,
        @Parameter(hidden = true) final ServerWebExchange exchange
    ) {
        Mono<Void> result = Mono.empty();
        exchange.getResponse().setStatusCode(HttpStatus.NOT_IMPLEMENTED);
        for (MediaType mediaType : exchange.getRequest().getHeaders().getAccept()) {
            if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
                String exampleString = "{ \"photoUrls\" : [ \"photoUrls\", \"photoUrls\" ], \"name\" : \"doggie\", \"id\" : 10, \"category\" : { \"name\" : \"Dogs\", \"id\" : 1 }, \"tags\" : [ { \"name\" : \"name\", \"id\" : 0 }, { \"name\" : \"name\", \"id\" : 0 } ], \"status\" : \"available\" }";
                result = ApiUtil.getExampleResponse(exchange, mediaType, exampleString);
                break;
            }
            if (mediaType.isCompatibleWith(MediaType.valueOf("application/xml"))) {
                String exampleString = "<pet> <id>10</id> <name>doggie</name> <photoUrls> <photoUrls>aeiou</photoUrls> </photoUrls> <tags> </tags> <status>aeiou</status> </pet>";
                result = ApiUtil.getExampleResponse(exchange, mediaType, exampleString);
                break;
            }
        }
        return result.then(Mono.empty());

    }

For the time being it is picked up by spring using the following class which implements the generated interface.

@RestController
public class PetController implements PetApi {

}


I tried looking around however could not find any obvious solutions/configurations which would solve the problem. Let me know if I overlooked something.


PS: I am using spring-boot v2.7.1 with openjdk-17

0 Answers
Related