5

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

Marc
  • 16,170
  • 20
  • 76
  • 119
Guy Hudara
  • 247
  • 4
  • 13
  • Are you getting the swagger definition generated behind "v2/api-docs" path? – Shibashis Jun 05 '16 at 16:25
  • Yes. But it contains only the /error path. It does not recognize the WidgetController. Why? – Guy Hudara Jun 06 '16 at 07:06
  • 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 Jun 06 '16 at 15:13
  • 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 Jun 08 '16 at 08:53

3 Answers3

12

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.

Dilip Krishnan
  • 5,417
  • 3
  • 37
  • 53
8

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.

senyor
  • 332
  • 4
  • 9
4

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

Francois
  • 1,851
  • 1
  • 13
  • 15
  • so with the code above, is it required to download the swagger-ui zip file and extract it somewhere on classpath? – jkerak Aug 16 '16 at 19:31
  • oh definitely, you need to serve the one page app swagger UI from somewhere, the above is just to get swagger json spec generated. – Francois Sep 08 '16 at 14:02