7

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: enter image description here

And this type is useless in my API operation: enter image description here

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

Honza Zidek
  • 9,204
  • 4
  • 72
  • 118
adnan_e
  • 1,764
  • 2
  • 16
  • 25

3 Answers3

6

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.

enter image description here

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.

adnan_e
  • 1,764
  • 2
  • 16
  • 25
  • Works great thanks. Yet i had to add this the docket configuration ,genericModelSubstitutes(Mono.class, Flux.class, Publisher.class) To be able correct show correctly the parameters – cabaji99 Mar 20 '20 at 23:19
  • It's too much work. Use `springdoc-openapi-webflux-ui` instead of `springfox` and you will have the functionality out-of-the-box. See my answer https://stackoverflow.com/a/69298342/2886891 – Honza Zidek Sep 23 '21 at 10:34
2

You can also follow Sprinfox example:

  • declare a RecursiveAlternateTypeRule.java
  • 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)))));
        }
    }
    
Mica
  • 51
  • 2
1

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
            ;
    }
}

Honza Zidek
  • 9,204
  • 4
  • 72
  • 118