Swagger 2 is the defacto API documentation tool that is used in many Spring boot applications. Most of the examples for using swagger with spring boot are with springMVC. There are certain use cases where you would be using spring boot with JAX-RS. Is it possible to use Swagger 2 with SpringBoot and JAX-RS applications?
While Swagger 2 and SpringMVC integration is straight forward with spring annotations, integration with JAX-RS annotations is a bit involved. We are going to look at how to do this.
This starts with a simple spring boot project.
Download the project and setup your workspace. This sample is done using IntelliJ IDEA. Now lets add the following dependencies. Notice that I have switched to spring boot 1.2.2 for this example.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jersey</artifactId> <version>1.2.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>1.2.2.RELEASE</version> </dependency> <!-- swagger and spring fox dependencies --> <dependency> <groupId>io.swagger</groupId> <artifactId>swagger-jersey2-jaxrs</artifactId> <version>1.5.13</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.7.0</version> </dependency>
Create classes and packages as shown in the image below.
Lets look at the classes in detail.
DocApplication is the main class that loads the application, defines the SpringFox Docket and ApiInfo objects. The @EnableSwagger2 annotation is required to use Swagger 2. The api() would be called internally when spring boot app is loaded and this in turn calls the apiInfo() along with setting the path and resources to be scanned for Swagger annotations.
package com.atech.spring.swagger.doc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.web.SpringBootServletInitializer; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.service.StringVendorExtension; import springfox.documentation.service.VendorExtension; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; import java.util.ArrayList; import java.util.Collection; /** * Created by atech on 1/21/18. */ @SpringBootApplication @EnableSwagger2 public class DocApplication extends SpringBootServletInitializer { public static void main(String[] args) { SpringApplication.run(DocApplication.class, args); } public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.basePackage("com.atech.spring.swagger.doc.controller")) .paths(PathSelectors.any()) .build() .enable(true) .apiInfo(apiInfo()); } public ApiInfo apiInfo() { StringVendorExtension vendorExtension = new StringVendorExtension("", ""); Collection<VendorExtension> vendorExtensions = new ArrayList<>(); vendorExtensions.add(vendorExtension); Contact contactInfo = new Contact("AtechRef", "www.atechref.com", "atechsoft@gmail.com"); return new ApiInfo( "SpringBoot-Swagger2-JaxRS", "Example project showing how to integrate spring boot " + "web app using jaxrs instead of springmvc with swagger and springfox.", "1.0", "For Demo only", contactInfo, "Apache 2.0", "www.apache.org", vendorExtensions); } }
The second class ties in jersey with swagger as shown below.
package com.atech.spring.swagger.doc; import io.swagger.jaxrs.config.BeanConfig; import io.swagger.jaxrs.config.SwaggerConfigLocator; import io.swagger.jaxrs.config.SwaggerContextService; import io.swagger.jaxrs.listing.ApiListingResource; import org.glassfish.jersey.server.ResourceConfig; import org.springframework.stereotype.Component; import javax.ws.rs.ApplicationPath; /** * Created by atech on 1/21/18. */ @Component @ApplicationPath("/api") public 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()); } }
The final class that is required to be configured is the CombinedResourceProvider.java that defines springfox binding
package com.atech.spring.swagger.doc; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; import springfox.documentation.swagger.web.InMemorySwaggerResourcesProvider; import springfox.documentation.swagger.web.SwaggerResource; import springfox.documentation.swagger.web.SwaggerResourcesProvider; import javax.annotation.Resource; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; /** * Created by atech on 1/21/18. */ @Component @Primary public class CombinedResourceProvider implements SwaggerResourcesProvider { @Resource private InMemorySwaggerResourcesProvider inMemorySwaggerResourcesProvider; 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()); } }
The controller is below and note the annotations in bold. Using the @Api, @ApiOperation and @ApiResponse annotations allows us to document the API in the code like java docs.
package com.atech.spring.swagger.doc.controller; import com.atech.spring.swagger.doc.model.Hello; import io.swagger.annotations.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; /** * Created by atech on 1/21/18. */ @Component @Path("/v1/hello") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Api(value = "Hello API - say hello to the world", produces = "application/json") public class HelloWorld { private static final Logger LOGGER = LoggerFactory.getLogger(HelloWorld.class); @GET //JAX-RS Annotation @Path("/{name}") //JAX-RS Annotation @ApiOperation( //Swagger Annotation value = "Say hello by entering your name", response = Hello.class) @ApiResponses(value = { //Swagger Annotation @ApiResponse(code = 200, message = "Resource found"), @ApiResponse(code = 404, message = "Resource not found") }) public Response sayHello(@ApiParam @PathParam("name") String name) { LOGGER.info("v1/hello/{} - {}", name, MediaType.APPLICATION_JSON); return this.buildResponse(name, MediaType.APPLICATION_JSON); } private Response buildResponse(String name, String via) { //handle 404 if ("404".equals(name)) { return Response.status(Response.Status.NOT_FOUND).build(); } Hello result = new Hello(); result.setMsg(String.format("Hello %s - %s", name, via)); return Response.status(Response.Status.OK).entity(result).build(); } }
And the model class is below
package com.atech.spring.swagger.doc.model; /** * Created by atech on 1/21/18. */ public class Hello { private String msg; public String getMsg() { return this.msg; } public void setMsg(String msg) { this.msg = msg; } }
Now when this app is built and run we can access swagger UI using http://localhost:8080/swagger-ui.html. It should look like below.
Expand the API and review the documentation that was entered in the controller code in the java file render in a nice UI . You can even try out the API by entering the required information and submitting a request that is processed in real time by the spring boot app.
How cool is that? We have a fully functional Swagger 2 UI that works with only annotations for SpringBoot and JAX-RS RESTful service.