如何配置 springfox 以解包 Mono 和 Flux 等反应类型,而无需在 @ApiResponse 中显式指定响应类型

How to configure springfox to unwrap reactive types such as Mono and Flux without having to explicitly specify response type in @ApiResponse

我正在使用 springfox 3.0.0 进行响应式支持,并且在我的 swagger 配置中使用 @EnableSwagger2WebFlux

我的swagger配置如下:

@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());
}

我有一个简单的控制器,如下图:

  @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 ...
    }

springfox生成文档时,有如下类型:

而这个类型在我的 API 操作中没有用:

我知道我可以通过在@ApiOperation 中指定响应类型来解决这个问题,但我正在努力避免这种情况,例如

@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
) {

我不喜欢这种方法,因为它是一个手动过程,因此容易出错。 我想要一些自动方法来进行以下转换:

Flux<T> -> T[] (since flux emits 0...N elements)
Mono<T> -> T
ResponseEntity<T> -> T

当然它必须是递归的(例如Mono<ResponseEntity<Flux<T>>> -> T)。

我浏览了 springfox 的代码,试图找到一些自定义类型解析的入口点,幸运的是它有一个可以从外部注入的 HandlerMethodResolver

我在我的 swagger 配置中添加了这个解析器的自定义实现 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;
        }
    };
}

这正是我需要的。

自动将Mono<ResponseEntity<Flux<PartnerDTO>>>转换为PartnerDTO[]Mono<ResponseEntity<Mono<PartnerDTO>>>自动转换为PartnerDTO

编辑::我更改了此实现以将 Flux 转换为 T[],因为它从一开始就应该如此。

您也可以按照 Sprinfox 示例:

  • 声明一个RecursiveAlternateTypeRule.java
  • 将其作为 alternateTypeRules 添加到您的 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,看起来,。请改用 springdoc-openapispringfox 我们也遇到了其他问题,不仅缺少 webflux 支持,而且我们很高兴地切换到 springdoc-openapi

对于webflux应用,你只需要add dependency springdoc-openapi-webflux-ui:

<dependency>
      <groupId>org.springdoc</groupId>
      <artifactId>springdoc-openapi-webflux-ui</artifactId>
      <version>1.5.10</version>
</dependency>

无需额外配置,MonoFlux 将开箱即用。

还有一个manual如何从springfox迁移到springdoc-openapi