I'm using springfox 3.0.0 for reactive support, and am using @EnableSwagger2WebFlux
on my swagger config.
My swagger config is as follows:
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage(basePackage))
.paths(PathSelectors.any())
.build()
.securityContexts(Lists.newArrayList(securityContext()))
.securitySchemes(Lists.newArrayList(apiKey()))
.globalOperationParameters(operationParameters());
}
I have a simple controller, as shown below:
@CrossOrigin(origins = "*", allowedHeaders = "*", maxAge = 3600)
@RestController
@RequestMapping("/")
public class ApiController {
@ApiOperation(value = "get all partners", authorizations = {@Authorization(value = "Bearer")})
@RequestMapping(value = "/partner",
method = RequestMethod.GET,
produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}
)
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Request succeeded")
})
public Mono<ResponseEntity<Flux<PartnerDTO>>> getAllPartners(
@ApiIgnore ServerHttpRequest httpRequest
) {
return ...
}
When springfox generates the documentation, it has the following type:
And this type is useless in my API operation:
I know I can fix this by specifying the response type in @ApiOperation, but I'm trying to avoid that, e.g.
@ApiOperation(value = "get all partners", authorizations = {@Authorization(value = "Bearer")})
@RequestMapping(value = "/partner",
method = RequestMethod.GET,
produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}
)
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Request succeeded", response = PartnerDTO.class)
})
public Mono<ResponseEntity<Flux<PartnerDTO>>> getAllPartners(
@ApiIgnore ServerHttpRequest httpRequest
) {
I don't like this approach as it's a manual process and and thus prone to errors. I'd like some automatic way to do the following conversion:
Flux<T> -> T[] (since flux emits 0...N elements)
Mono<T> -> T
ResponseEntity<T> -> T
And of course it would have to be recursive (e.g. Mono<ResponseEntity<Flux<T>>> -> T
).
springdoc is a much more recent library that does not have so much legacy code as Springfox. As a consequence of the first one, springdoc is actually updated regularly and the amount of open issues is relatively small (only 11 vs 270 on Springfox).
Springfox is a framework that acts as the “glue” between Swagger and Spring. It generates the specification (contract) based on your code and also deploys the Swagger UI client with your application, allowing you to immediately test your REST API.
To enable the Swagger2 in Spring Boot application, you need to add the following dependencies in our build configurations file. For Gradle users, add the following dependencies in your build. gradle file. Now, add the @EnableSwagger2 annotation in your main Spring Boot application.
I went through the code of springfox trying to find some entry point for custom type resolving, and luckily it has a HandlerMethodResolver
which can be injected externally.
I added a custom implementation of this resolver in my swagger config class:
@Bean
@Primary
public HandlerMethodResolver fluxMethodResolver(TypeResolver resolver) {
return new HandlerMethodResolver(resolver) {
@Override
public ResolvedType methodReturnType(HandlerMethod handlerMethod) {
var retType = super.methodReturnType(handlerMethod);
// we unwrap Mono, Flux, and as a bonus - ResponseEntity
while (
retType.getErasedType() == Mono.class
|| retType.getErasedType() == Flux.class
|| retType.getErasedType() == ResponseEntity.class
) {
if ( retType.getErasedType() == Flux.class ) {
// treat it as an array
var type = retType.getTypeBindings().getBoundType(0);
retType = new ResolvedArrayType(type.getErasedType(), type.getTypeBindings(), type);
} else {
retType = retType.getTypeBindings().getBoundType(0);
}
}
return retType;
}
};
}
Which does exactly what I need.
It automatically converts Mono<ResponseEntity<Flux<PartnerDTO>>>
to PartnerDTO[]
, and Mono<ResponseEntity<Mono<PartnerDTO>>>
to PartnerDTO
.
EDIT:: I changed this implementation to convert Flux to T[], as it should have been from the start.
You can also follow Sprinfox example:
add it as alternateTypeRules in your Swagger Configuration
@Configuration
@EnableSwagger2WebFlux
public abstract class AbstractSwaggerConfiguration {
@Autowired
private TypeResolver resolver;
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2) //
.select().apis(RequestHandlerSelectors.any()).paths(PathSelectors.any()) //
.build()
.alternateTypeRules(new RecursiveAlternateTypeRule(resolver,
Arrays.asList(AlternateTypeRules.newRule(resolver.resolve(Mono.class, WildcardType.class), resolver.resolve(WildcardType.class)),
AlternateTypeRules.newRule(resolver.resolve(ResponseEntity.class, WildcardType.class), resolver.resolve(WildcardType.class)))))
.alternateTypeRules(
new RecursiveAlternateTypeRule(resolver, Arrays.asList(AlternateTypeRules.newRule(resolver.resolve(Flux.class, WildcardType.class), resolver.resolve(List.class, WildcardType.class)),
AlternateTypeRules.newRule(resolver.resolve(ResponseEntity.class, WildcardType.class), resolver.resolve(WildcardType.class)))));
}
}
Springfox
, as it seems, missed the boat. Use springdoc-openapi
instead. We have had also other issues with springfox
, not only the missing webflux
support, and we happily switched to springdoc-openapi
For webflux applications, all you need is to add dependency springdoc-openapi-webflux-ui
:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webflux-ui</artifactId>
<version>1.6.9</version>
</dependency>
No additional configuration is needed, and the Mono
and Flux
will be unwrapped out-of-the-box.
There is also a manual how to migrate from springfox
to springdoc-openapi
.
If you need to programmatically customize the configuration, you define an OpenAPI
bean like this:
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
@Configuration
public class OpenApi30Config {
@Bean
public OpenAPI openApi() {
return new OpenAPI()
.info(new Info().title("My Application")
.description("This is my application")
.version("1.0")
)
// and lot of other configuration possible here
;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With