如何配置 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-openapi
。 springfox
我们也遇到了其他问题,不仅缺少 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>
无需额外配置,Mono
和 Flux
将开箱即用。
还有一个manual如何从springfox
迁移到springdoc-openapi
。
我正在使用 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-openapi
。 springfox
我们也遇到了其他问题,不仅缺少 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>
无需额外配置,Mono
和 Flux
将开箱即用。
还有一个manual如何从springfox
迁移到springdoc-openapi
。