5
votes

I have a spring boot application with jersey and gradle, and I am trying to automatically generate the API documentation using springfox.

I have followed the steps here: http://springfox.github.io/springfox/docs/current/

Here is what I did:

  • build.gradle:

    dependencies {
        .........
        //Swagger
        compile "io.springfox:springfox-swagger2:2.4.0"
        compile "io.springfox:springfox-bean-validators:2.4.0"
        compile 'io.springfox:springfox-swagger-ui:2.4.0'
    }
    
  • Spring boot Application:

    @SpringBootApplication
    @EnableSwagger2
    public class AnalyzerServiceApplication{
    
    public static void main(String[] args) {
        SpringApplication.run(AnalyzerServiceApplication.class, args);
    }
    
    @Bean
    public Docket analyzerApi() {
    return new Docket(DocumentationType.SWAGGER_2)
    .select()
        .apis(RequestHandlerSelectors.any())
        .paths(PathSelectors.any())
        .build()
    .pathMapping("/")
    .directModelSubstitute(LocalDate.class, String.class)
    .genericModelSubstitutes(ResponseEntity.class)
    .alternateTypeRules(
        newRule(typeResolver.resolve(DeferredResult.class,
        typeResolver.resolve(ResponseEntity.class, WildcardType.class)),
        typeResolver.resolve(WildcardType.class)))
    .useDefaultResponseMessages(false)
    .globalResponseMessage(RequestMethod.GET,
        newArrayList(new ResponseMessageBuilder()
            .code(500)
            .message("500 message")
            .responseModel(new ModelRef("Error"))
            .build()))
    .securitySchemes(newArrayList(apiKey()))
    .securityContexts(newArrayList(securityContext()))
    .enableUrlTemplating(true)
    .globalOperationParameters(
        newArrayList(new ParameterBuilder()
            .name("someGlobalParameter")
            .description("Description of someGlobalParameter")
            .modelRef(new ModelRef("string"))
            .parameterType("query")
            .required(true)
            .build()))
        .tags(new Tag("Pet Service", "All apis relating to pets")) 
        ;
    }
    
    @Autowired
    private TypeResolver typeResolver;
    
    private ApiKey apiKey() {
        return new ApiKey("mykey", "api_key", "header");
    }
    
    private SecurityContext securityContext() {
        return SecurityContext.builder()
            .securityReferences(defaultAuth())
            .forPaths(PathSelectors.regex("/anyPath.*"))
            .build();
    }
    
    List<SecurityReference> defaultAuth() {
        AuthorizationScope authorizationScope
            = new AuthorizationScope("global", "accessEverything");
            AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        return newArrayList(
            new SecurityReference("mykey", authorizationScopes));
    }
    
    @Bean
    SecurityConfiguration security() {
        return new SecurityConfiguration(
            "test-app-client-id",
            "test-app-client-secret",
            "test-app-realm",
            "test-app",
            "apiKey",
            ApiKeyVehicle.HEADER, 
            "api_key", 
            "," /*scope separator*/);
    }
    
    @Bean
    UiConfiguration uiConfig() {
        return new UiConfiguration("validatorUrl");
    }
    
  • Now the controller (Jersey)

    @Api(value = "/widget")
    @Path("/widget")
    @Component
    public class WidgetController extends BaseController {
    
    @Autowired
    private WidgetService widgetService;
    
    @GET
    @Path("/secHealth")
    @ApiOperation(value = "Find pet by ID", notes = "Returns a pet when ID < 10.  ID > 10 or nonintegers will simulate API error conditions", response = Pet.class)
    @ApiResponses(value = { @ApiResponse(code = 400, message = "Invalid ID supplied"),
    @ApiResponse(code = 404, message = "Pet not found") })
    public Response getPet() {
        //Do something
    }
    

When I start the server and navigate to http://localhost:8080/swagger-ui.html, I can see the "green" UI screen with only the basic-error-controller listed there. My own controller is not there.

What did I do wrong? Thanks Guy

3
Are you getting the swagger definition generated behind "v2/api-docs" path? - Shibashis
Yes. But it contains only the /error path. It does not recognize the WidgetController. Why? - Guy Hudara
Are you sure springfox supports Jersey Controllers? The documentation says it supports Spring mvc... Probably by default it is looking for Spring @Controller annotation... - jny
I have not sure at all that springfox supports Jersey. I have used the @Contoller annotation with no luck. Do you know any other module (other than springfox) which I can use with spring-boot, jersey and gradle? - Guy Hudara

3 Answers

11
votes

As of version 2.5.0 springfox only supports spring-mvc controllers. Jax-rs implementations like jersey aren't supported.

The current alternative to using springfox is to use the swagger-core library for jax-rs/jersey based services.

It does have the hooks needed to implement support for jersey in 2.6+. Here is an excerpt of a way to implement it in this issue

Currently ResourceConfig has a method called "getClasses" which will list everything registerted. like Resources, Filters,etc... Maybe this could help. But be aware that the returning classes could also be filters or any other stuff you could register with jersey2.

6
votes

To be able to see Jersey methods from Springfox swagger UI:

  1. Configure your Swagger with Jersey following https://github.com/swagger-api/swagger-core/wiki/Swagger-Core-Jersey-2.X-Project-Setup-1.5
  2. Configure Springfox Swagger following http://springfox.github.io/springfox/docs/current/
  3. Add in you SpringBoot application configuration class (annotated with @Configuration):

    @Value("${springfox.documentation.swagger.v2.path}") private String swagger2Endpoint;

  4. In application.properties add reference to your Jersey swagger.json:

    springfox.documentation.swagger.v2.path=/{change it to your Jersey api path}/swagger.json

Now you should be able to see Jersey Swagger generated api from Springfox Swagger UI page.

3
votes

Thanks @Dilip-Krishnan for the springfox update and @Guy-Hudara for the question, I came up with the following solution to get swagger support in my springboot jersey powered app :

import io.swagger.jaxrs.config.BeanConfig;
import io.swagger.jaxrs.listing.ApiListingResource;
import io.swagger.jaxrs.listing.SwaggerSerializers;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

/**
 * As of version 2.5.0 springfox only supports spring-mvc controllers. Jax-rs implementations like jersey aren't supported.
 *
 * Fortunately io.swagger::swagger-jersey2-jaxrs::1.5.3 have the hooks needed to implement support for jersey in 2.6+.
 *
 * some pointers I used to get this swagger config done and swagger-core, springboot and jersey integrated:
 * http://stackoverflow.com/questions/37640863/springfox-swagger-no-api-docs-with-spring-boot-jersey-and-gardle
 * https://www.insaneprogramming.be/blog/2015/09/04/spring-jaxrs/
 * https://github.com/swagger-api/swagger-core/wiki/Swagger-Core-Jersey-2.X-Project-Setup-1.5#adding-the-dependencies-to-your-application
 *
 */
@Configuration
public class SwaggerConfiguration {

    @Autowired
    ResourceConfig resourceConfig;

    @PostConstruct
    public void configure() {

        resourceConfig.register(ApiListingResource.class);
        resourceConfig.register(SwaggerSerializers.class);

        BeanConfig beanConfig = new BeanConfig();
        beanConfig.setVersion("1.0.2");
        beanConfig.setSchemes(new String[]{"http"});
        beanConfig.setHost("localhost:8888");
        beanConfig.setBasePath("/api");
        beanConfig.setResourcePackage("com.my.resource");
        beanConfig.setPrettyPrint(true);
        beanConfig.setScan(true);

    }
}

That worked out great for me