Search results
Documenting multiple REST API versions using Spring Boot, Jersey and Swagger
1. OVERVIEW
A few days ago I was completing the accompanying source code for Microservices using Spring Boot, Jersey, Swagger and Docker blog entry and found some problems while adding documentation support to multiple implementation versions of the “Hello” JAX-RS resource endpoints where the version information was passed in the URL or Accept Header.
Recapping the details, I stated my belief that Swagger’s BeanConfig singleton instantiation approach wasn’t going to work. The reason I believe so is because a BeanConfig instance dynamically creates only one Swagger definition file and it would require one definition file for each version. Yes, there are some “hacks” to get away with it, like removing the endpoints implementation where the version is passed in the Accept Header and then only one Swagger definition file will include the documentation for every endpoint implementation version. I don’t think this is the right answer though.
This post and its accompanying source code helps you how to generate multiple Swagger definition files to feed Swagger UI to display JAX-RS APIs documentation for multiple implementation versions.
2. REQUIREMENTS
- Java 7+.
- Maven 3.2+.
- Familiarity with Spring Framework.
I’ll follow same steps as described in Microservices using Spring Boot, Jersey, Swagger and Docker to create the initial project but would only proceed with the explanation of the new additions to this post.
3. CREATE THE HELLO WORLD SERVICE
The accompanying source code has been refactored to match the packages as described in this post.
Add the entry point to the application via start-class property and Swagger dependency to pom.xml.
This is a snippet of the app’s entry point, that allows this application to be built and run as either a fat jar or as a war in a Servlet Container.
4. IMPLEMENT MULTIPLE VERSIONS OF THE JAX-RS ENDPOINTS USING JERSEY
At this point spring-boot-starter-jersey and spring-boot-starter-web are already dependencies of the application, this is important because they need to be mapped to different paths, Jersey’s will provide access to the Resources endpoints while Spring MVC will provide access to Actuator endpoints, for more detailed information, please visit Microservices using Spring Boot, Jersey, Swagger and Docker
JAX-RS providers, endpoints, etc.. are registered in a subclass of Jersey’s ResourceConfig class.
It could be noticed there is not reference to Swagger’s BeanConfig as it was in previous blog post.
Next comes the HelloWorldV1 (Version 1) resource implementation which include both JAX-RS and Swagger annotations:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.asimio.swaggerexample.rest.v1;
...
@Component
@Path("/")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Api(value = "Hello resource", produces = "application/json")
public class HelloResourceV1 {
private static final Logger LOGGER = LoggerFactory.getLogger(HelloResourceV1.class);
@GET
@Path("v1/hello/{name}")
@ApiOperation(value = "Gets a hello resource. Version 1 - (version in URL)", response = Hello.class)
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Hello resource found"),
@ApiResponse(code = 404, message = "Hello resource not found")
})
public Response getHelloVersionInUrl(@ApiParam @PathParam("name") String name) {
LOGGER.info("getHelloVersionInUrl() v1");
return this.getHello(name, "Version 1 - passed in URL");
}
@GET
@Path("hello/{name}")
@Consumes("application/vnd.asimio-v1+json")
@Produces("application/vnd.asimio-v1+json")
@ApiOperation(value = "Gets a hello resource. World Version 1 (version in Accept Header)", response = Hello.class)
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Hello resource found"),
@ApiResponse(code = 404, message = "Hello resource not found")
})
public Response getHelloVersionInAcceptHeader(@PathParam("name") String name) {
LOGGER.info("getHelloVersionInAcceptHeader() v1");
return this.getHello(name, "Version 1 - passed in Accept Header");
}
private Response getHello(String name, String partialMsg) {
if ("404".equals(name)) {
return Response.status(Status.NOT_FOUND).build();
}
Hello result = new Hello();
result.setMsg(String.format("Hello %s. %s", name, partialMsg));
return Response.status(Status.OK).entity(result).build();
}
...
}
HelloWorldV2 (Version 2) resource implementation is similar and the source code could be found in accompanying repository at https://bitbucket.org/asimio/multiversion-api-jersey-swagger-example
Notice /hello/{name} endpoint is versioned through URL and Accept Header, what’s needed now is to generate Swagger definition files for both versions to documents the endpoints.
5. DOCUMENT THE REST APIs WITH SWAGGER
Swagger definition files are going to be generated at build time using a Maven plugin which would need to be added to pom.xml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
...
<properties>
<swagger-maven-plugin.version>3.1.3</swagger-maven-plugin.version>
</properties>
...
<plugin>
<groupId>com.github.kongchen</groupId>
<artifactId>swagger-maven-plugin</artifactId>
<version>${swagger-maven-plugin.version}</version>
<configuration>
<apiSources>
<!-- Version 1 -->
<apiSource>
<springmvc>false</springmvc>
<locations>com.asimio.swaggerexample.rest.v1</locations>
<schemes>http,https</schemes>
<basePath>/api</basePath>
<info>
<title>Multiversion Spring Boot + Jersey + Swagger Demo (Version 1)</title>
<version>v1</version>
<description>A multi-version demo (version 1) of a RESTful service using Spring Boot, Jersey and Swagger.</description>
<termsOfService>http://www.github.com/kongchen/swagger-maven-plugin</termsOfService>
<contact>
<email>ootero@asimio.net</email>
<name>Orlando L Otero</name>
<url>http://tech.asimio.net</url>
</contact>
<license>
<url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
<name>Apache 2.0</name>
</license>
</info>
<outputFormats>json</outputFormats>
<swaggerDirectory>${basedir}/target/classes/static/v1</swaggerDirectory>
<swaggerApiReader>com.github.kongchen.swagger.docgen.reader.JaxrsReader</swaggerApiReader>
</apiSource>
<!-- Version 2 -->
...
</apiSources>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<dependencies>
<!-- Adding dependency to swagger-hibernate-validations to enable the BeanValidator as a custom model converter -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-hibernate-validations</artifactId>
<version>${swagger.version}</version>
</dependency>
</dependencies>
</plugin>
...
I’ll add a brief explanation of the elements configured with this plugin just for Version 1 since Version 2 is similar.
Starting from line 12:
springmvc | the type of API being documented, false for JAX-RS, true for Spring MVC |
locations | comma-separated list of packages containing classes with @Api Swagger annotation |
schemes | comma-separated list of protocols on which API are available |
basePath | the base path on which the API are available. Jersey’s servlet path in this case |
info | information about the API, nested elements will be used by Swagger UI |
outputFormats | format in which Swagger definition file will be generated, either json or yaml |
swaggerDirectory | directory where this plugin will generate the Swagger definition file |
For more information about these configuration elements, please visit the plugin home page at https://github.com/kongchen/swagger-maven-plugin
Next download Swagger UI zip file from https://github.com/swagger-api/swagger-ui/releases, 2.1.4 was used for the example app bundled with this post.
Extract and move resulting content to src/main/resources/static.
Create directories v1 and v2 in src/main/resources/static and place a copy of previously extracted index.html in them, then remove the one found in static folder.
Update index.html located in both, static/v1 and static/v2 directories for Swagger UI to find Swagger definition files.
Replace:
with:
in …/static/v1/index.html and:
in …/static/v2/index.html.
Since there aren’t any Swagger defintion file yet, the application would run but there wouldn’t be any documentation available, let’s generate them:
swagger-maven-plugin is bounded to Maven compile phase, which will generate json definition files in target/classes/static/v1 and target/classes/static/v2 as configured in pom.xml and will be included in the resulting jar application along with their corresponding index.html.
6. RUNNING THE APPLICATION
Accessing the endpoints:
Create resource - Version 1 in Accept Header
Create resource - Version 2 in Accept Header
Some screenshots taken from running the application:
-
API avaliable versions API available versions
-
Create Hello resource - Version 1 in Accept Header Spring Boot, Jersey, Swagger - Create resource - Version in Accept Header
-
Get Hello resource - Version 2 in Accept Header Spring Boot, Jersey, Swagger - Get resource - Version in Accept Header
Thanks for reading and as always, feedback is very much appreciated. If you found this post helpful and would like to receive updates when content like this gets published, sign up to the newsletter.
7. SOURCE CODE
Accompanying source code for this blog post can be found at: