Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I'm trying to figure out how manage two (or more) version of my API endpoints using Spring Fox.

To version my APIs, I'm using the Versioning through content negotiation, also know as Versioning using Accept header. The versions of each endpoint are controlled individually using the header information. Per example, for the version one I use the attribute produces:

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

For version two, I use:

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

I not use consumes for the first (v1) version, so if the client use only application/json on the call the first version will be called by default.

I would like to show the two version on the Swagger UI. How to do that?

like image 285
Dherik Avatar asked Dec 08 '25 11:12

Dherik


2 Answers

It's very simple. Just create one Docket for each version.

Example, the first version:

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

And for version two:

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

The secret here is filter the available endpoints by the produces attribute.

The Swagger-UI will show the two versions on the combo:

enter image description here

This code needs to be on a class annotated with @Configuration. You also need to enable the Swagger with @EnableSwagger2.

like image 118
Dherik Avatar answered Dec 11 '25 06:12

Dherik


As mentioned by Dherik you can create Docket for each version. But to filter here I have tried using Predicate and custom controller annotations.

  1. Configuration class annotated with @Configuration and @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. Custom Annotation class : SwaggerDocV30

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public  @interface SwaggerDocV30 {
    }
    
  3. Custom Annotation class : SwaggerDocV31

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public  @interface SwaggerDocV31 {
    }
    
  4. Finally annotate your controllers with @SwaggerDocV30 or @SwaggerDocV31

     @SwaggerDocV30
     @Controller
     public class MyController extends AbstractController {}
    

    Or

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

    image

like image 42
Manish Avatar answered Dec 11 '25 06:12

Manish