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.

Providing Custom API Information

You may override the default API information provided like this:

@Bean
public Docket petApi() {
	ApiInfo info = new ApiInfo("My API", "API description", "1.0.0", "http://terms-of-service.url",
			"Micha Kops <nospam@hascode.com>", "License", "http://licenseurl");
 
	return new Docket(DocumentationType.SWAGGER_2).apiInfo(info).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()));
}

Afterwards our Swagger UI screen looks like this one:

Providing custom API information.

Providing custom API information.

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:

Additional REST articles of mine

Please feel free to have a look at these tutorials of mine covering different aspects of handling or creating RESTful webservices.

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

Article Updates

  • 2015-08-06: Links to other REST articles of mine added.
  • 2015-10-22: Link list updated
  • 2015-11-24: Example for providing custom API information added.

Tags: , , , , , , , , , , , ,

11 Responses to “Integrating Swagger into a Spring Boot RESTful Webservice with Springfox”

  1. Vidhyadharan Says:

    hi , i like your article , I am in need to generate swagger json while building (compile) maven .
    could you please suggest

  2. micha kops Says:

    Hi Vidhyadharan,

    perhaps springfox-staticdocs might help you here: http://springfox.github.io/springfox/docs/current/#configuring-springfox-staticdocs

  3. johnnym Says:

    Hi, thanks a lot! I think the best walkthrough on the net right now! Great work!

  4. micha kops Says:

    Thanks, you’re welcome! :)

  5. SOORAJ Says:

    Hi, as I see in one screenshot, it prints spring boot specific paths also (such as basic error controller etc). Is there a way to get rid of those, so that Swagger Spec contains only the business operation specific path and related details (i.e, /currentdate/{pattern} alone in this example)?

  6. hitesh bargujar Says:

    The API Doc specification always show contact info as APACHE2.0, how I can override the value for the same.
    Mostly I am talking about @SwaggerDefinition

  7. micha kops Says:

    Hi Hitesh,

    I have updated the tutorial with an example of providing a custom API information :)

    Cheers,

    Micha

  8. Bryan Says:

    Hi Micha,
    Thanks for the article. It’s great. I am unable to use the code running spring-boot 1.3.2, but only if I change the CurrentDateController.formatCurrentDate() method to return a string.

    I keep getting this annoying error if I try and let it return a FormattedDate. I tried putting in a dependency in the build.gradle for ‘com.fasterxml.jackson.core:jackson-databind:2.5.5′
    and for ‘com.fasterxml.jackson.core:jackson-core:2.5.5′, but the magic didn’t happen.

    I’d like to understand the mapping of types to JSON object in Spring-boot better. Any pointers, or tips on how to fix the code?

    Thanks,
    BR

  9. Praveen Says:

    Hi Micha,

    How to pass the value entered in the input box for api_key as query parameter for every request

    Thanks
    Praveen

  10. Mahadev Shindhe Says:

    Search google for swagger and spring boot. This is the best site i got it. Thanks.

  11. saaddev Says:

    Hello , i try to use ApiKey to deny access for users without apikey value to the swagger-ui, with a same configuration with yours it does not work ! SOS and sorry for my english :s

Search
Tags
Categories