Search code examples
javajax-rsswaggerwordnikspringfox

SpringFox not finding jax-rs endpoints


After solving Using Springfox to document jax-rs services in a Spring app, I now find that SpringFox's JSON reply doesn't show any APIs:

{
  "swagger": "2.0",
  "info": {
    "description": "Some description",
    "version": "1.0",
    "title": "My awesome API",
    "contact": {
      "name": "my-email@domain.org"
    },
    "license": {}
  },
  "host": "localhost:9090",
  "basePath": "/myapp"
}

Here's springfox-servlet.xml:

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean class="com.wordnik.swagger.jaxrs.listing.ApiListingResourceJSON" />
    <bean class="com.wordnik.swagger.jaxrs.listing.ApiDeclarationProvider" />
    <bean class="com.wordnik.swagger.jaxrs.listing.ResourceListingProvider" />
</beans>

This is in a properties file:

swagger.resourcePackage=org.myapp

Swagger is configured to find the implementation classes using the reflective jax-rs scanner:

@Component
public class SwaggerConfiguration {

    @Value("${swagger.resourcePackage}")
    private String resourcePackage;

    @PostConstruct
    public void init() {
        ReflectiveJaxrsScanner scanner = new ReflectiveJaxrsScanner();
        scanner.setResourcePackage(resourcePackage);
        ScannerFactory.setScanner(scanner);

        ClassReaders.setReader(new DefaultJaxrsApiReader());

        SwaggerConfig config = ConfigFactory.config();
        config.setApiVersion(apiVersion);
        config.setBasePath(basePath);
    }

    public String getResourcePackage() {
        return resourcePackage;
    }

    public void setResourcePackage(String resourcePackage) {
        this.resourcePackage = resourcePackage;
    }
}

Here's the documentation configuration:

@Configuration
@EnableSwagger2
public class ApiDocumentationConfiguration {
    @Bean
    public Docket documentation() {
        System.out.println("=========================================== Initializing Swagger");
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build()
                .pathMapping("/")
                .apiInfo(metadata());
    }

    @Bean
    public UiConfiguration uiConfig() {
        return UiConfiguration.DEFAULT;
    }

    private ApiInfo metadata() {
        return new ApiInfoBuilder()
                .title("My awesome API")
                .description("Some description")
                .version("1.0")
                .contact("my-email@domain.org")
                .build();
    }
}

And here's a sample class with the api annotations:

@Api(value = "activity")
@Service
@Path("api/activity")
@Produces({ MediaType.APPLICATION_JSON })
public class ActivityService {

    @Autowired
    private CommandExecutor commandExecutor;
    @Autowired
    private FetchActivityCommand fetchActivityCommand;

    @ApiOperation(value = "Fetch logged-in user's activity", httpMethod = "GET", response = Response.class)
    @GET
    @Path("/mine")
    @Consumes(MediaType.TEXT_PLAIN)
    @Produces(MediaType.APPLICATION_JSON)
    @Authorization(rejectionMessage = Properties.Authorization.NOT_LOGGED_IN_MESSAGE_PREFIX + "view your activities.")
    public List<Activity> listMyActivities(@Context HttpServletResponse response, @Context HttpServletRequest request) throws IOException {
        return buildActivityList(response, (UUID) request.getSession().getAttribute(Properties.Session.SESSION_KEY_USER_GUID));
    }
    ...
}

Why isn't it exposing the API? Would using the wordnik swagger library solve this, or improve the solution?


Solution

  • By default SpringFox will document your REST services implemented using Spring MVC - it is as simple as adding @EnableSwagger2 annotation

    @SpringBootApplication
    @EnableSwagger2
    public class Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    
    }
    

    I managed to get SpringFox working with JAX-RS At first I have added some swagger dependencies along with SpringFox:

    dependencies {
        compile("org.springframework.boot:spring-boot-starter-web")
        compile("org.springframework.boot:spring-boot-starter-jersey")
        compile("io.springfox:springfox-swagger-ui:2.4.0")
        compile("io.springfox:springfox-swagger2:2.4.0")
        compile("io.swagger:swagger-jersey2-jaxrs:1.5.8")
        testCompile("junit:junit")
    }
    

    Then I have enabled JAX-RS (Jersey) with Swagger in my spring-boot application:

    @Component
    @ApplicationPath("/api")
    public static class JerseyConfig extends ResourceConfig {
    
        public JerseyConfig() {
    
            BeanConfig swaggerConfig = new BeanConfig();
            swaggerConfig.setBasePath("/api");
            SwaggerConfigLocator.getInstance().putConfig(SwaggerContextService.CONFIG_ID_DEFAULT, swaggerConfig);
    
            packages(getClass().getPackage().getName(), ApiListingResource.class.getPackage().getName());
        }
    
    }
    

    Please note that all JAX-RS endpoints will be under /api context - otherwise it would conflict with Spring-MVC dispatcher

    Finally we should add the swagger json generated for Jersey endpoints to Springfox:

    @Component
    @Primary
    public static class CombinedSwaggerResourcesProvider implements SwaggerResourcesProvider {
    
        @Resource
        private InMemorySwaggerResourcesProvider inMemorySwaggerResourcesProvider;
    
        @Override
        public List<SwaggerResource> get() {
    
            SwaggerResource jerseySwaggerResource = new SwaggerResource();
            jerseySwaggerResource.setLocation("/api/swagger.json");
            jerseySwaggerResource.setSwaggerVersion("2.0");
            jerseySwaggerResource.setName("Jersey");
    
            return Stream.concat(Stream.of(jerseySwaggerResource), inMemorySwaggerResourcesProvider.get().stream()).collect(Collectors.toList());
        }
    
    }
    

    That's it! Now your JAX-RS endpoints will be documented by Swagger. I used following sample endpoint:

    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import org.springframework.stereotype.Component;
    
    import javax.ws.rs.GET;
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;
    import javax.ws.rs.core.MediaType;
    
    @Component
    @Path("/hello")
    @Api
    public class Endpoint {
    
        @GET
        @ApiOperation("Get message")
        @Produces(MediaType.TEXT_PLAIN)
        public String message() {
            return "Hello";
        }
    
    }
    

    Now when you start your server and go to http://localhost:8080/swagger-ui.html you will see the documentation for JAX-RS endpoints. Use combobox on top of the page to switch to the documentation of Spring MVC endpoints

    enter image description here