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
).
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.