Spring REST + Swagger 2 Example
June 24, 2019
This page will walk through Spring REST and Swagger 2 integration with annotation and XML example. Swagger is API specification for documentation in web service. Swagger can generate documentation, API clients and server stubs in different languages by parsing Swagger definition. Swagger also provides UI that represents documentation. Swagger provides specification and we will use SpringFox implementation in our example. Using Swagger it becomes easy to understand REST web service methods for a client because using Swagger UI we get all information of web service methods with description about what the method does. We can also test web services method from Swagger UI. In our example we will integrate Swagger in our Spring REST web service application to get REST web service method documentation. We can also test REST web service methods using Swagger UI. In our example we are using SpringFox implementation of Swagger 2 specification. Here on this page we will provide complete example to integrate Swagger 2 with Spring REST web service using JavaConfig as well as XML configuration. Now find the complete example step by step.
Contents
- Technologies Used
- Step-1: Gradle and Maven to Resolve Swagger API
- Step-2: Enable Swagger 2 using @EnableSwagger2 Annotation
- Step-3: Swagger UI Endpoint Configuration
- Docket Custom Implementation
- Swagger Annotations for Documentation from Property File Lookup
- Spring REST + Swagger 2 Integration Example using JavaConfig
- Spring REST + Swagger 2 Integration Example using XML Configuration
- Test Demo Application
- Reference
- Download Source Code
Technologies Used
We are using following software in our example.1. Java 8
2. Spring 4.3
3. Swagger 2
4. Gradle 3.3
5. Maven 3.3
6. Tomcat 8.0
7. Eclipse Mars
Step-1: Gradle and Maven to Resolve Swagger API
To work with Swagger documentation, we need to usespringfox-swagger2
and springfox-swagger-ui
API.
Find the Gradle to resolve Swagger API dependency.
dependencies { compile 'io.springfox:springfox-swagger2:2.7.0' compile 'io.springfox:springfox-swagger-ui:2.7.0' }
<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>
Step-2: Enable Swagger 2 using @EnableSwagger2 Annotation
To enable Swagger 2 we need to annotate configuration class with@EnableSwagger2
.
@Configuration @EnableSwagger2 public class SwaggerConfig { }
@EnableSwagger2
annotation. For custom implementation, we need to create a method with return type Docket
annotated with @Bean
.
In case we are using XML configuration, we need to create a bean of the above class.
<bean class="com.concretepage.config.SwaggerConfig"/>
Step-3: Swagger UI Endpoint Configuration
Find the configuration to expose Swagger UI endpoint/swagger-ui.html
and /v2/api-docs
in Spring MVC configuration file.
Find the configuration using JavaConfig. We need to override
addResourceHandlers()
method of WebMvcConfigurerAdapter
. If we are not using Spring boot, our Spring MVC configuration class needs to be annotated with @EnableWebMvc
. Now find the addResourceHandlers()
method definition.
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("swagger-ui.html") .addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/"); }
<mvc:resources mapping="swagger-ui.html" location="classpath:/META-INF/resources/"/> <mvc:resources mapping="/webjars/**" location="classpath:/META-INF/resources/webjars/"/>
spring-app
then we need to append /swagger-ui.html
to get Swagger UI and append /v2/api-docs
to get JSON response of API documentation.
1. URL for Swagger UI.
http://localhost:8080/spring-app/swagger-ui.html
http://localhost:8080/spring-app/v2/api-docs
Docket Custom Implementation
Docket
is a builder that provides default configuration of Swagger documentation. If we want custom implementation, then we need to create a method in Swagger configuration class with return type Docket
annotated with @Bean
.
@Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket customImplementation(){ return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.basePackage("com.concretepage")) .paths(PathSelectors.any()) .build(); } }
Docket
we are passing configuration for Swagger 2. select()
method returns ApiSelectorBuilder
that is used to control end points. apis()
method allows selection of request handler. Request handler can be any
, none
, basePackage
etc. The method paths()
is used to allow selection of path using predicate. In our example we are using any
predicate that is default. After configurations, we need to build the selector using build()
method. If we want to set custom meta information, we need use apiInfo()
method as following.
@Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket customImplementation(){ return new Docket(DocumentationType.SWAGGER_2) .apiInfo(getApiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.concretepage")) .paths(PathSelectors.any()) .build(); } private ApiInfo getApiInfo() { return new ApiInfo("REST Api Documentation", "REST Api Documentation", "1.0", "urn:tos", new Contact("", "", ""), "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList<VendorExtension>()); } }
Swagger Annotations for Documentation from Property File Lookup
Swagger provides annotation to provide custom documentation for REST web service methods. Documentation messages can be externalized and can be looked up from property file. Find the annotations.@ApiOperation: Provides documentation for REST web service method, for example what operation this method performs.
@ApiParam: Provides documentation about parameter of REST web service method.
Find the example.
@GetMapping("article/{id}") @ApiOperation(value = "Get Article by Id", notes = "${ArticleController.getArticleById.notes}") public ResponseEntity<Article> getArticleById( @ApiParam(value = "${ArticleController.getArticleById.id}", required = true) @PathVariable("id") Integer id) { Article article = articleService.getArticleById(id); return new ResponseEntity<Article>(article, HttpStatus.OK); }
documentation.properties
ArticleController.getArticleById.notes= Pass article Id and the REST web service method will return an article. ArticleController.getArticleById.id= The values of Id can be 1,2 etc. ArticleController.getArticleById.id.default= 1
@ApiImplicitParams: This is the wrapper for
@ApiImplicitParam
annotation.
@ApiImplicitParam: Represents a single parameter in an API operation and is used within
@ApiImplicitParams
to provide documentation for REST web service method parameter. @ApiParam
is also used to provide documentation but it is bound to JAX-RS parameter.
Find the example.
@PutMapping("article") @ApiOperation(value = "Update Article", notes = "${ArticleController.updateArticle.notes}") @ApiImplicitParams( @ApiImplicitParam(name="article", value="${ArticleController.updateArticle.article}") ) public ResponseEntity<Article> updateArticle(@RequestBody Article article) { articleService.updateArticle(article); return new ResponseEntity<Article>(article, HttpStatus.OK); }
ArticleController.updateArticle.notes= This REST web service method will update article. ArticleController.updateArticle.article= Pass article with all fields.
Spring REST + Swagger 2 Integration Example using JavaConfig
Find the project structure.apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'war' war.archiveName 'spring-app.war' repositories { mavenCentral() } dependencies { compile 'org.springframework.boot:spring-boot-starter-web:1.5.6.RELEASE' compile 'io.springfox:springfox-swagger2:2.7.0' compile 'io.springfox:springfox-swagger-ui:2.7.0' providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat:1.5.6.RELEASE' }
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.concretepage</groupId> <artifactId>spring-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>Spring</name> <description>Spring Demo Project</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.6.RELEASE</version> <relativePath/> </parent> <properties> <java.version>1.8</java.version> <context.path>spring-app</context.path> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>3.0.0</version> <configuration> <warName>${context.path}</warName> </configuration> </plugin> </plugins> </build> </project>
package com.concretepage.config; import java.util.ArrayList; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.service.VendorExtension; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; @Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket customImplementation(){ return new Docket(DocumentationType.SWAGGER_2) .apiInfo(getApiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.concretepage")) .paths(PathSelectors.any()) .build(); } private ApiInfo getApiInfo() { return new ApiInfo("REST Api Documentation", "REST Api Documentation", "1.0", "urn:tos", new Contact("", "", ""), "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList<VendorExtension>()); } }
package com.concretepage.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; @Configuration @ComponentScan("com.concretepage") @EnableWebMvc @PropertySource("classpath:documentation.properties") public class AppConfig extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("swagger-ui.html") .addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/"); } }
package com.concretepage.config; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class[] { AppConfig.class }; } @Override protected Class<?>[] getServletConfigClasses() { return null; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } }
package com.concretepage.domain; public class Article { private int articleId; private String title; private String category; public Article() {} public Article(int articleId, String title, String category) { this.articleId = articleId; this.title = title; this.category = category; } public int getArticleId() { return articleId; } public void setArticleId(int articleId) { this.articleId = articleId; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getCategory() { return category; } public void setCategory(String category) { this.category = category; } }
package com.concretepage.service; import java.util.List; import com.concretepage.domain.Article; public interface IArticleService { List<Article> getAllArticles(); Article getArticleById(int articleId); boolean addArticle(Article article); void updateArticle(Article article); void deleteArticle(int articleId); }
package com.concretepage.service; import java.util.ArrayList; import java.util.List; import java.util.function.Predicate; import org.springframework.stereotype.Service; import com.concretepage.domain.Article; @Service public class ArticleService implements IArticleService { private List<Article> list = new ArrayList<>(); { list.add(new Article(1, "Angular 2 Tutorial using CLI", "Angular")); list.add(new Article(2, "Spring Boot Getting Started", "Spring Boot")); list.add(new Article(3, "Lambda Expressions Java 8 Example", "Java 8")); } @Override public Article getArticleById(int articleId) { Predicate<Article> articlePredicate = a-> a.getArticleId() == articleId; Article obj = list.stream().filter(articlePredicate).findFirst().get(); return obj; } @Override public List<Article> getAllArticles(){ return list; } @Override public boolean addArticle(Article article){ list.add(article); return true; } @Override public void updateArticle(Article article) { Predicate<Article> articlePredicate = a-> a.getArticleId() == article.getArticleId(); Article obj = list.stream().filter(articlePredicate).findFirst().get(); obj.setTitle(article.getTitle()); obj.setCategory(article.getCategory()); } @Override public void deleteArticle(int articleId) { Predicate<Article> articlePredicate = a-> a.getArticleId() == articleId; Article obj = list.stream().filter(articlePredicate).findFirst().get(); list.remove(obj); } }
package com.concretepage.controller; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.util.UriComponentsBuilder; import com.concretepage.domain.Article; import com.concretepage.service.IArticleService; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; @Controller @RequestMapping("user") public class ArticleController { @Autowired private IArticleService articleService; @GetMapping("article/{id}") @ApiOperation(value = "Get Article by Id", notes = "${ArticleController.getArticleById.notes}") public ResponseEntity<Article> getArticleById( @ApiParam(value = "${ArticleController.getArticleById.id}", required = true) @PathVariable("id") Integer id) { Article article = articleService.getArticleById(id); return new ResponseEntity<Article>(article, HttpStatus.OK); } @PutMapping("article") @ApiOperation(value = "Update Article", notes = "${ArticleController.updateArticle.notes}") @ApiImplicitParams( @ApiImplicitParam(name="article", value="${ArticleController.updateArticle.article}") ) public ResponseEntity<Article> updateArticle(@RequestBody Article article) { articleService.updateArticle(article); return new ResponseEntity<Article>(article, HttpStatus.OK); } @GetMapping("articles") public ResponseEntity<List<Article>> getAllArticles() { List<Article> list = articleService.getAllArticles(); return new ResponseEntity<List<Article>>(list, HttpStatus.OK); } @PostMapping("article") public ResponseEntity<Void> addArticle(@RequestBody Article article, UriComponentsBuilder builder) { boolean flag = articleService.addArticle(article); if (flag == false) { return new ResponseEntity<Void>(HttpStatus.CONFLICT); } HttpHeaders headers = new HttpHeaders(); headers.setLocation(builder.path("/article/{id}").buildAndExpand(article.getArticleId()).toUri()); return new ResponseEntity<Void>(headers, HttpStatus.CREATED); } @DeleteMapping("article/{id}") public ResponseEntity<Void> deleteArticle(@PathVariable("id") Integer id) { articleService.deleteArticle(id); return new ResponseEntity<Void>(HttpStatus.NO_CONTENT); } }
ArticleController.getArticleById.notes= Pass article Id and the REST web service method will return an article. ArticleController.getArticleById.id= The values of Id can be 1,2 etc. ArticleController.getArticleById.id.default= 1 ArticleController.updateArticle.notes= This REST web service method will update article. ArticleController.updateArticle.article= Pass article with all fields.
Spring REST + Swagger 2 Integration Example using XML Configuration
Find the project structure.<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" 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.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.concretepage" /> <mvc:resources mapping="swagger-ui.html" location="classpath:/META-INF/resources/"/> <mvc:resources mapping="/webjars/**" location="classpath:/META-INF/resources/webjars/"/> <bean class="com.concretepage.config.SwaggerConfig"/> <bean name="jackson2ObjectMapper" class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"> <property name="indentOutput" value="true"/> </bean> <mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="objectMapper" ref="jackson2ObjectMapper" /> </bean> </mvc:message-converters> </mvc:annotation-driven> </beans>
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>Spring 4 REST Example</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/dispatcher-servlet.xml </param-value> </context-param> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>
Test Demo Application
To build and run the demo application, follow the steps.1. Download the source code from the link given below on this page.
2. Go to the root directory of the project using command prompt.
3. Build the project using gradle with following command.
gradle clean build
mvn clean package
4. Deploy the WAR file in tomcat.
a. URL for Swagger UI.
http://localhost:8080/spring-app/swagger-ui.html
http://localhost:8080/spring-app/v2/api-docs