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