Integrating Swagger into a Spring Boot RESTful Webservice with Springfox

July 1st, 2015 by

Spring Boot allows us to create RESTful web-services with ease, Swagger specifies a format to describe the capabilities and operations of these services and with Swagger UI it is possible to explore our REST API with a nice graphical user interface in our browser.

Springfox is a project that aims at creating automated JSON API documentation for API’s built with Spring and is used in the following tutorial to integrate Swagger into a sample application.

Swagger UI for Spring Boot RESTful Webservices.

Swagger UI for Spring Boot RESTful Webservices.

 

Creating our RESTful Webservice with Spring Boot

With Spring Boot our RESTful web-service is set up in no time.

Our restful web-service allows a user to return the current date in a customized format as a JSON structure, in addition an increasing counter is returned, too.

An example: If the user specifies the format yyyy, our webservice returns the current year with 4 digits e.g. 2015.

For further details about creating RESTful web-services with Spring Boot I highly recommend reading the excellent tutorial from Spring.io: “Building a RESTful Webservice“.

Gradle Configuration

This is our build.gradle.

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.4.RELEASE")
    }
}
 
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'
 
jar {
    baseName = 'dateconv-rest-service'
    version =  '1.0.0'
}
 
repositories {
    mavenCentral()
    jcenter()
}
 
sourceCompatibility = 1.8
targetCompatibility = 1.8
 
dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
    compile("io.springfox:springfox-swagger2:2.0.2")
    compile("io.springfox:springfox-swagger-ui:2.0.2")
    testCompile("junit:junit")
}
 
task wrapper(type: Wrapper) {
    gradleVersion = '2.3'
}

DateFormat Value Object

This POJO encapsulates the formatted date, the counter and the user’s date pattern.

package com.hascode.tutorial;
 
public class FormattedDate {
	private final long id;
	private final String dateString;
	private final String pattern;
 
	public FormattedDate(final long id, final String pattern, final String dateString) {
		this.id = id;
		this.pattern = pattern;
		this.dateString = dateString;
	}
 
	// getters ommitted
}

Resource Controller

The controller defines a RESTful web-service with an entry point URL pattern “/currentdate/{pattern}” where pattern marks the path parameter to specify the desired date-format.

Finally the controller creates and returns a new value object with the formatted date:

package com.hascode.tutorial;
 
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.atomic.AtomicLong;
 
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
public class CurrentDateController {
 
	private final AtomicLong counter = new AtomicLong();
 
	@RequestMapping("/currentdate/{pattern}")
	public FormattedDate formatCurrentDate(@PathVariable final String pattern) {
		DateTimeFormatter fmt = DateTimeFormatter.ofPattern(pattern);
 
		return new FormattedDate(counter.incrementAndGet(), pattern, fmt.format(LocalDateTime.now()));
	}
}

Spring Application Container

All we need to run the RESTful web-service is the main method, the other configuration is used to setup swagger for our application.

With the additional code we use Springfox to..

  • enable Springfox Swagger 2
  • specify where the Spring API controllers are located
  • setup a new Docket (Docket is Springfox primary configuration mechanism)
  • setup paths, security, API tokens and all the stuff

As I don’t want to copy and paste, I’d highly recommend to have a look at the Springfox Reference Documentation where everything is explained very nice and in detail.

package com.hascode.tutorial;
 
import static com.google.common.collect.Lists.newArrayList;
import static springfox.documentation.schema.AlternateTypeRules.newRule;
 
import java.time.LocalDate;
import java.util.List;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.request.async.DeferredResult;
 
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.schema.WildcardType;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.SecurityConfiguration;
import springfox.documentation.swagger.web.UiConfiguration;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
 
import com.fasterxml.classmate.TypeResolver;
 
@SpringBootApplication
@EnableSwagger2
@ComponentScan(basePackageClasses = { CurrentDateController.class })
public class Application {
 
	public static void main(final String[] args) {
		SpringApplication.run(Application.class, args);
	}
 
	@Bean
	public Docket petApi() {
		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()));
	}
 
	@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-realm", "test-app", "apiKey");
	}
 
	@Bean
	UiConfiguration uiConfig() {
		return new UiConfiguration("validatorUrl");
	}
}

Directory Structure

Our project structure should look like this one by now:

.
├── build.gradle
└── src
    └── main
        └── java
            └── com
                └── hascode
                    └── tutorial
                        ├── Application.java
                        ├── CurrentDateController.java
                        └── FormattedDate.java

Exploring the Application

Now we’re ready to boot our web-service and explore Swagger and Swagger UI…

Starting with Gradle

Using Gradle, out application is booted within a few seconds using the following command:

$ gradle bootRun
[..]
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.2.4.RELEASE)
 
2015-07-01 08:27:50.673  INFO 13044 --- [           main] com.hascode.tutorial.Application         : Starting Application on styx with PID 13044 (/data/project/spring-boot-swagger-tutorial/build/classes/main started by soma in /data/project/spring-boot-swagger-tutorial)
[..]
2015-07-01 08:27:52.765  INFO 13044 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/currentdate/{pattern}],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public com.hascode.tutorial.FormattedDate com.hascode.tutorial.CurrentDateController.formatCurrentDate(java.lang.String)
2015-07-01 08:27:52.766  INFO 13044 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/v2/api-docs],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.http.ResponseEntity<springfox.documentation.spring.web.json.Json> springfox.documentation.swagger2.web.Swagger2Controller.getDocumentation(java.lang.String)
2015-07-01 08:27:52.769  INFO 13044 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/configuration/security],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto org.springframework.http.ResponseEntity<springfox.documentation.swagger.web.SecurityConfiguration> springfox.documentation.swagger.web.ApiResourceController.securityConfiguration()
2015-07-01 08:27:52.769  INFO 13044 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/configuration/ui],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto org.springframework.http.ResponseEntity<springfox.documentation.swagger.web.UiConfiguration> springfox.documentation.swagger.web.ApiResourceController.uiConfiguration()
2015-07-01 08:27:52.769  INFO 13044 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/swagger-resources],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto org.springframework.http.ResponseEntity<java.util.List<springfox.documentation.swagger.web.SwaggerResource>> springfox.documentation.swagger.web.ApiResourceController.swaggerResources()
2015-07-01 08:27:52.772  INFO 13044 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[text/html],custom=[]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest)
2015-07-01 08:27:52.773  INFO 13044 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
[..]
2015-07-01 08:27:53.201  INFO 13044 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2015-07-01 08:27:53.203  INFO 13044 --- [           main] com.hascode.tutorial.Application         : Started Application in 2.723 seconds (JVM running for 3.036)

Afterwards all necessary services are available to us..

Calling the RESTful Webservice

First of all we’d like to try our RESTful web-service, available at http://localhost:8080/currentdate/PATTERN where PATTERN is a valid date format.

Examples:

  • http://localhost:8080/currentdate/yyyy
  • http://localhost:8080/currentdate/yyyy-MM-dd
Accessing the RESTful Webservice

Accessing the RESTful Webservice

Swagger Definition Export

Swagger creates a complete service documentation for us that may be used to generate REST-clients for our services in no time (e.g. using an online editor like Swagger Editor).

We may access the documentation in JSON format at: http://localhost:8080/v2/api-docs

Swagger API Definition

Swagger API Definition

This is a excerpt from the service documentation:

{
  "swagger": "2.0",
  "info": {
    "description": "Api Documentation",
    "version": "1.0",
    "title": "Api Documentation",
    "termsOfService": "urn:tos",
    "contact": {
      "name": "Contact Email"
    },
    "license": {
      "name": "Apache 2.0",
      "url": "http:\/\/www.apache.org\/licenses\/LICENSE-2.0"
    }
  },
  "host": "localhost:8080",
  "basePath": "\/",
  "tags": [
    {
      "name": "current-date-controller",
      "description": "Current Date Controller"
    },
    {
      "name": "basic-error-controller",
      "description": "Basic Error Controller"
    }
  ]
  [..]
}

Swagger UI

Swagger UI allows us to explore our REST API and play around with its services.

We  may access it at: http://localhost:8080/swagger-ui.html

Swagger UI for Spring Boot RESTful Webservices.

Swagger UI for Spring Boot RESTful Webservices.

Swagger for JAX-RS based Webservices

For this scenario please feel free to have a look at my blog article: “Documenting RESTful Webservices in Swagger, AsciiDoc and Plain Text with Maven and the JAX-RS Analyzer“.

Testing RESTful Webservices

Please feel free to have a look at the following blog articles of mine:

Tutorial Sources

Please feel free to download the tutorial sources from my Bitbucket repository, fork it there or clone it using Git:

git clone https://bitbucket.org/hascode/springboot-swagger-springfox-tutorial.git

Resources

Search
Tags
Categories