如何使用 Spring Boot 设置 SpringFox 显示两个(或更多)版本的 Rest API?

How set SpringFox to show two (or more) versions of the Rest API using Spring Boot?

我正在尝试弄清楚如何使用 Spring Fox 管理我的 API 端点的两个(或更多)版本。

为了对我的 API 进行版本控制,我使用的是 Versioning through content negotiation, also know as Versioning using Accept header。使用 header 信息单独控制每个端点的版本。例如,对于第一个版本,我使用属性 produces:

@Override
@PostMapping(
        produces = "application/vnd.company.v1+json")
public ResponseEntity<User> createUser(

对于版本二,我使用:

@Override
@PostMapping(
        produces = "application/vnd.company.v2+json",
        consumes = "application/vnd.company.v2+json")
public ResponseEntity<User> createUserVersion2(

我没有对第一个 (v1) 版本使用 consumes,因此如果客户端在调用时仅使用 application/json,默认情况下将调用第一个版本。

我想在 Swagger 上展示这两个版本 UI。怎么做?

很简单。只需为每个版本创建一个 Docket。

例子,第一个版本:

@Bean
public Docket customImplementation(
        @Value("${springfox.documentation.info.title}") String title,
        @Value("${springfox.documentation.info.description}") String description) {

    return new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(apiInfo(title, description, "1.0"))
            .groupName("v1")
            .useDefaultResponseMessages(false)
            .securitySchemes(newArrayList(apiKey()))
            .pathMapping("/api")
            .securityContexts(newArrayList(securityContext())).select()
            .apis(e -> Objects.requireNonNull(e).produces().parallelStream()
                    .anyMatch(p -> "application/vnd.company.v1+json".equals(p.toString())))
            .paths(PathSelectors.any())
            .build();
}

对于版本二:

@Bean
public Docket customImplementationV2(
        @Value("${springfox.documentation.info.title}") String title,
        @Value("${springfox.documentation.info.description}") String description) {

        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo(title, description, "2.0"))
                .groupName("v2")
                .select()
                .apis(e -> Objects.requireNonNull(e).produces()
                        .parallelStream()
                        .anyMatch(p -> "application/vnd.company.v2+json".equals(p.toString())))
                .build();
}

这里的秘诀是通过 produces 属性过滤可用端点。

Swagger-UI 将在组合中显示两个版本:

此代码需要在 class 上用 @Configuration 注释。您还需要使用 @EnableSwagger2.

启用 Swagger

如 Dherik 所述,您可以为每个版本创建 Docket。但是为了在这里进行过滤,我尝试使用 Predicate 和自定义控制器注释。

  1. 配置 class 注释为 @Configuration@EnableSwagger2

     import com.google.common.base.Predicate;
    
     @Bean
     public Docket apiV30() {
         return new Docket(DocumentationType.SWAGGER_2)
             .groupName("v30")
             .select()
             .apis(selectorV30())
             .paths(PathSelectors.any()).build().apiInfo(apiEndPointsInfo());
     }
    
     private Predicate<RequestHandler> selectorV30(){
         return new Predicate<RequestHandler>() {
             @Override
             public boolean apply(RequestHandler input) {
                 return input.findControllerAnnotation(SwaggerDocV30.class).isPresent();
             }
         };
     }
    
     @Bean
     public Docket apiV31() {
         return new Docket(DocumentationType.SWAGGER_2)
             .groupName("v31")
             .select()
             .apis(selectorV31())
             .paths(PathSelectors.any()).build().apiInfo(apiEndPointsInfo());
     }
    
     private Predicate<RequestHandler> selectorV31(){
         return new Predicate<RequestHandler>() {
             @Override
             public boolean apply(RequestHandler input) {
                 return input.findControllerAnnotation(SwaggerDocV31.class).isPresent();
             }
         };
     }
    
  2. 自定义注释class:SwaggerDocV30

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public  @interface SwaggerDocV30 {
    }
    
  3. 自定义注释class:SwaggerDocV31

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public  @interface SwaggerDocV31 {
    }
    
  4. 最后用@SwaggerDocV30 或@SwaggerDocV31 注释您的控制器

     @SwaggerDocV30
     @Controller
     public class MyController extends AbstractController {}
    

     @SwaggerDocV31
     @Controller
     public class MyController extends AbstractController {}]