© Balaji Varanasi and Maxim Bartkov 2022
B. Varanasi, M. BartkovSpring RESThttps://doi.org/10.1007/978-1-4842-7477-4_6

6. Documenting REST Services

Balaji Varanasi1   and Maxim Bartkov2
(1)
Salt Lake City, UT, USA
(2)
Kharkov, Ukraine
 
In this chapter we will discuss the following:
  • The basics of Swagger

  • Using Swagger for API documentation

  • Customizing Swagger

Documentation is an important aspect of any project. This is especially true for enterprise and open-source projects, where many people collaborate to build the project. In this chapter, we will look at Swagger, a tool that simplifies REST API documentation.

Documenting a REST API for consumers to use and interact with is a difficult task because there are no real established standards. Organizations have historically relied on manually edited documents to expose REST contracts to clients. With SOAP-based web services, a WSDL serves as a contract for the client and provides a detailed description of the operations and associated request/response payloads. The WADL, or Web Application Description Language, specification tried to fill this gap in the REST web services world, but it didn’t get a lot of adoption. In recent years, there has been growth in the number of metadata standards such as Swagger, Apiary, and iODocs for describing REST services. Most of them grew out of the need to document APIs, thereby expanding an API’s adoption.

Swagger

Swagger (http://swagger.io) is a specification and a framework for creating interactive REST API documentation. It enables documentation to be in sync with any changes made to REST services. It also provides a set of tools and SDK generators for generating API client code. Swagger was originally developed by Wordnik in early 2010 and is currently backed by SmartBear software.

Swagger is a language-agnostic specification with implementations available for a variety of languages such as Java, Scala, and PHP. A full description of specifications can be found at https://github.com/springfox/springfox. The specification is made up of two file types—a resource listing file and a set of API declaration files that describe the REST API and the available operations.

The resource listing file referred to by the name “api-docs” is the root document for describing the API. It contains general information about the API such as the API version, title, description, and license. As the name suggests, the resource listing file also contains all of the API resources available in the application. Listing 6-1 shows a sample resource listing file for a hypothetical REST API. Notice that Swagger uses JSON as its description language. From the APIs array in Listing 6-1, you can see that the resource listing file has two API resources declared, namely, products and orders. The URIs /default/products and /default/orders allow you to access the resource’s API declaration file. Swagger allows grouping of its resources; by default, all resources are grouped under the default group and, hence, the “/default” in the URI. The info object contains the contact and licensing information associated with the API.
{
    "apiVersion": "1.0",
    "swaggerVersion": "1.2"
    "apis": [
                {
                        "description": "Endpoint for Product management",
                        "path": "/default/products"
                },
                {
                        "description": "Endpoint for Order management",
                        "path": "/default/orders"
                }
        ],
        "authorizations": { },
        "info" : {
        "contact": "[email protected]",
        "description": "Api for an ecommerce application",
        "license": "Apache 2.0",
        "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.html",
        "termsOfServiceUrl": "Api terms of service",
        "title": "ECommerce App"
    }
}
Listing 6-1

Sample Resource File

An API declaration file describes a resource along with the API operations and request/response representations. Listing 6-2 shows a sample API declaration file for the product resource and will be served at the URI /default/products. The basePath field provides the Root URI serving the API. The resourcePath specifies the resource path relative to the basePath. In this case, we are specifying that the product’s REST API is accessible at http://server:port/products. The APIs field contains API objects that describe an API operation. Listing 6-2 describes one API operation called createProduct and its associated HTTP method, the media type of the messages consumed/produced, and API responses. The models field contains any model objects associated with the resource. Listing 6-2 shows a product model object associated with a product resource.
{
    "apiVersion": "1.0",
    "swaggerVersion": "1.2"
    "basePath": "/",
    "resourcePath": "/products",
    "apis": [
           {
               "description": "createProduct",
               "operations": [
                {
                     "method": "POST",
                     "produces": [ "application/json" ],
                     "consumes": [ "application/json" ],
                     "parameters": [ { "allowMultiple": false} ],
                        "responseMessages": [
                        {
                           "code": 200,
                           "message": null,
                           "responseModel": "object"
                        }
                        ]
                  }
                    ],
                   "path": "/products"
              }
        ],
    "models": {
                  "Product": {
                "description": "",
                "id": "Product",
                "properties": { }
               }
        }
}
Listing 6-2

Sample Products API Declaration File at /default/products

Note

In our hypothetical example, Swagger expects the API declaration file for the product resource to reside at the “/default/products” URI. This should not be confused with the actual REST API location for accessing the product resource. In this example, the declaration file indicates that the product resource is accessible at http://server:port/products URI.

Integrating Swagger

Integrating Swagger involves creating the “api-docs ” resource listing file and a set of API declaration files describing API’s resources. Instead of hand-coding these files, there are several Swagger and community-owned projects that integrate with existing source code and automatically generate these files. springfox-boot-starter is one such framework that simplifies Swagger integration with Spring MVC–based projects. We begin the Swagger integration with QuickPoll application by adding the springfox-boot-starter Maven dependency shown in Listing 6-3 in the pom.xml file.

Note

We continue our tradition of building on the work we did on the QuickPoll application in the previous chapters. You can also use the starter project available at Chapter6starter folder of the downloaded source code. The completed solution is available under the Chapter6final folder.

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-boot-starter</artifactId>
    <version>3.0.0</version>
</dependency>
Listing 6-3

Springfox-Boot-Starter Dependency

By the next step, we have to define bean Docket as shown in Listing 6-4.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
@Configuration
public class SwaggerConfiguration {
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .paths(PathSelectors.any())
                .build();
    }
}
Listing 6-4

Define Docket Bean

With this minimal configuration in place, run the QuickPoll application and launch the URI http://localhost:8080/v3/api-docs. You should see the resource listing file as shown in Figure 6-1.
../images/332520_2_En_6_Chapter/332520_2_En_6_Fig1_HTML.png
Figure 6-1

QuickPoll resource listing file

Swagger UI

The resource listing and API declaration files act as valuable resources for understanding a REST API. Swagger UI is a subproject of Swagger that takes these files and automatically generates a pleasant, intuitive interface for interacting with API. Using this interface, both technical and nontechnical folks can test REST services by submitting requests and see how those services respond. The Swagger UI is built using HTML, CSS, and JavaScript and doesn’t have any other external dependencies. It can be hosted in any server environment or can even run from your local machine.

The springfox-boot-starter already included work with Swagger UI that uses JSON from http://localhost:8080/v3/api-docs and parsing JSON in readable UI as shown in Figure 6-2.
../images/332520_2_En_6_Chapter/332520_2_En_6_Fig2_HTML.png
Figure 6-2

QuickPoll Swagger UI

Without some modifications, we are ready to launch Swagger UI. Run the quick-poll application and navigate to the URL http://localhost:8080/swagger-ui.html. You should see QuickPoll Swagger UI, as shown in Figure 6-2.

Using the UI, you should be able to perform operations such as creating polls and reading all polls.

Customizing Swagger

In the previous sections, you have seen that with minimal configuration, we were able to create interactive documentation using Swagger. Additionally, this documentation would automatically update itself when we make changes to our services. However, you will notice that out of the box, the title and the API descriptions are not very intuitive. Also, the URLs such as “Terms of Service,” “Contact the Developer,” and so on don’t work. As you explore the UI, the Response classes such as Poll and Vote are not visible in the Swagger UI, and the user has to end up guessing what the return type for the operations is going to be.

Swagger Springfox provides a convenient builder named Docket for customizing and configuring Swagger. The Docket provides convenient methods and sensible defaults but itself uses the ApiInfo class to perform the actual configuration. We begin our Swagger customization by creating a SwaggerConfig class under the com.apress package in our QuickPoll application. Populate the newly created class with the contents of Listing 6-5.
package com.apress;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import java.util.Collections;
@Configuration
public class SwaggerConfiguration {
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.apress.controller"))
                .paths(PathSelectors.any())
                .build()
                .apiInfo(apiInfo());
    }
    private ApiInfo apiInfo() {
        return new ApiInfo(
                "QuickPoll REST API",
                "QuickPoll Api for creating and managing polls",
                "http://example.com/terms-of-service",
                "Terms of service",
                new Contact("Maxim Bartkov", "www.example.com", "[email protected]"),
                "MIT License", "http://opensource.org/licenses/MIT", Collections.emptyList());
    }
}
Listing 6-5

Custom Swagger Implementation

The SwaggerConfig class is annotated with @Configuration indicating that it contains one or more Spring bean configurations. Because the Docket relies on the framework’s SpringSwaggerConfig, we inject an instance of SpringSwaggerConfig for later use. The SpringSwaggerConfig is a Spring-managed bean that gets instantiated during Spring’s component scanning in JAR files.

The configureSwagger method contains the meat of our Swagger configuration. The method is annotated with @Bean, indicating to Spring that the return value is a Spring bean and needs to be registered within a BeanFactory. The Swagger Springfox framework picks up this bean and customizes Swagger. We begin the method implementation by creating an instance of SwaggerSpringMvcPlugin. Then, using the ApiInfoBuilder, we create an ApiInfo object containing the title, description, contact, and license information associated with the QuickPoll application. Finally, we pass the created apiInfo and apiVersion information to the Docket instance and return it.

Note

It is possible to have multiple methods producing Docket beans . Each Docket would result in a separate resource listing. This is useful in situations in which you have the same Spring MVC application that serves more than one API or multiple versions of the same API.

With the new SwaggerConfig class added, run the QuickPoll application and navigate to http://localhost:8080/swagger-ui.html. You will see the changes reflected in our UI as shown in Figure 6-3.
../images/332520_2_En_6_Chapter/332520_2_En_6_Fig3_HTML.png
Figure 6-3

Updated QuickPoll Swagger UI

From Figure 6-3, you will notice that in addition to the three QuickPoll REST endpoints, there is a Spring Boot’s “/error” endpoint. Because this endpoint really doesn’t serve any purpose, let’s hide it from our API documentation. To accomplish this, we will use the Docket class’s handy includePattern method. The includePattern method allows us to specify which request mappings should be included in the resource listing. Listing 6-6 shows the updated portion of the SwaggerConfig’s configureSwagger method . The paths method by default takes regular expressions, and, in our case, we explicitly listed all three endpoints we would like to include.
docket
                .apiInfo(apiInfo)
                .paths(PathSelectors.regex("/polls/*.*|/votes/*.*|/computeresult/*.*"));
Listing 6-6

ConfigureSwagger Method with IncludePatterns

Rerun the QuickPoll application and you will see the Spring Boot’s error controller no longer appearing in the documentation.

Configuring Controllers

Swagger Core provides a set of annotations that make it easy to customize controller documentation. In this section, we will customize the PollController , but the same principles apply to other REST controllers. The downloaded code in Chapter6final has the complete customization of all controllers.

We begin by annotating the PollContoller with the @Api annotation as shown in Listing 6-7. The @Api annotation marks a class as a Swagger resource. Swagger scans classes annotated with @Api to read the metadata required for generating resource listing and API declaration files. Here we are indicating that the documentation associated with the PollController will be hosted at /polls. Remember that out of the box, Swagger used the Class name and generated URI poll-controller (http://localhost:8080/swagger-ui/index.html#!/poll-controller) to host the documentation. With our change, the PollController Swagger documentation is accessible at http://localhost:8080/swagger-ui.html#!/polls. Using the @Api annotation , we have also provided the description associated with our Poll API.
import io.swagger.annotations.Api;
@RestController
@Api(value = "polls", description = "Poll API")
public class PollController {
        // Implementation removed for brevity
}
Listing 6-7

@Api Annotation in Action

Run the QuickPoll application, and, on navigating to Swagger UI at http://localhost:8080/swagger-ui/index.html, you will notice the updated URI path and description as shown in Figure 6-4.
../images/332520_2_En_6_Chapter/332520_2_En_6_Fig4_HTML.png
Figure 6-4

Updated poll endpoint

Now we will move on to the API operation customization using the @ApiOperation annotation . This annotation allows us to customize the operation information such as name, description, and response. Listing 6-8 shows the @ApiOperation applied to the createPoll, getPoll, and getAllPolls methods. We use the value attribute to provide a brief description of the operation. Swagger recommends limiting this field to 120 characters. The notes field can be used to provide more descriptive information about the operation.
import io.swagger.annotations.ApiOperation;
@ApiOperation(value = "Creates a new Poll", notes="The newly created poll Id will be sent in the location response header", response = Void.class)
@PostMapping("/polls")
public ResponseEntity<Void> createPoll(@Valid @RequestBody Poll poll) {
        .......
}
@ApiOperation(value = "Retrieves a Poll associated with the pollId", response=Poll.class)
@GetMapping("/polls/{pollId}")
public ResponseEntity<?> getPoll(@PathVariable Long pollId) {
        .........
}
@ApiOperation(value = "Retrieves all the polls", response=Poll.class, responseContainer="List")
@GetMapping("/polls")
public ResponseEntity<Iterable<Poll>> getAllPolls() {
        ..........
}
Listing 6-8

@ApiOperation Annotated Methods

The createPoll method on successful completion sends an empty body and a status code 201 to the client. However, because we are returning a ResponseEntity, Swagger is not able to figure out the right response model. We fix this using ApiOperation’s response attribute and setting it to a Void.class. We also changed the method return type from ResponseEntity<?> to ResponseEntity<Void> to make our intent more clear.

The getPoll method returns a poll associated with the passed in pollId parameter. Hence, we set the ApiOperation’s response attribute to Poll.class. Because the getAllPolls method returns a collection of Poll instances, we have used the responseContainer attribute and set its value to List.

With these annotations added, rerun and launch QuickPoll application’s Swagger UI to verify that the descriptions, response model, and notes sections are changed. For example, click the “polls” link next to “Poll API” to expand the PollController’s operations. Then click the “/polls/{pollId}” link next to GET to see the response model associated with getPoll method. Figure 6-5 shows this updated response model.
../images/332520_2_En_6_Chapter/332520_2_En_6_Fig5_HTML.png
Figure 6-5

GetPoll method's updated model

The @ApiOperation we used earlier allows us to specify an operation’s default return type. As we have seen throughout the book, a well-defined API uses additional status codes, and Swagger provides the @ApiResponse annotation to configure the codes and associated response body. Listing 6-9 shows the createPoll method annotated with @ApiResponse for status codes 201 and 500. Swagger requires us to place all the @ApiResponse annotations inside a wrapper @ApiResponse annotation . With the status code 201, we have added notes indicating how to retrieve the newly created poll ID. With the status code 500, we have indicated that the response body will contain an ErrorDetail instance.
import com.wordnik.swagger.annotations.ApiResponse;
import com.wordnik.swagger.annotations.ApiResponses;
        @ApiOperation(value = "Creates a new Poll", notes="The newly created poll Id will be sent in the location response header", response = Void.class)
        @ApiResponses(value = {@ApiResponse(code=201, message="Poll Created Successfully", response=Void.class),
                        @ApiResponse(code=500, message="Error creating Poll", response=ErrorDetail.class) } )
       @PostMapping("/polls")
        public ResponseEntity<Void> createPoll(@Valid @RequestBody Poll poll) {
        // Content removed for brevity
}
Listing 6-9

@ApiResponse Annotations

Run the QuickPoll application and navigate to Swagger UI. Click the “polls” link next to “Poll API” to expand the PollController’s operations. Then click the “/polls” link next to POST to see the updated notes and ErrorDetail model schema. Figure 6-6 shows the expected output.
../images/332520_2_En_6_Chapter/332520_2_En_6_Fig6_HTML.png
Figure 6-6

Modified response messages

A quick glance at Figure 6-6 shows that we have more response than configured messages. This is because Swagger out of the box adds a set of default response messages for each HTTP method. This behavior can be disabled using the useDefaultResponseMessages method in the Docket class as shown in Listing 6-10.
public class SwaggerConfig {
        @Bean
        public Docket configureSwagger() {
            // Content removed
            docket.useDefaultResponseMessages(false);
            return docket;
        }
}
Listing 6-10

Ignore Default Response Messages

Run the QuickPoll application and repeat these steps to view the response messages associated with the POST operation on “/polls” URI. As shown in Figure 6-7, the default response messages are no longer displayed.
../images/332520_2_En_6_Chapter/332520_2_En_6_Fig7_HTML.jpg
Figure 6-7

Updated response messages

In addition to the configuration options we looked at, Swagger provides the following annotations to configure model objects:
  • @ApiModel—Annotation that allows changing the name of the model or providing a description to the associated model

  • @ApiModelProperty—Annotation that can be used to provide property description and list of allowed values and to indicate if it is required or not

Summary

Documentation plays an important role in understanding and consuming a REST API. In this chapter, we reviewed the basics of Swagger and integrated it with a QuickPoll application to generate interactive documentation. We also looked at customizing Swagger to meet our application-specific needs.

In the next chapter, we will look at techniques for versioning REST API and implementing paging and sorting capabilities.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.232.11