SpringBoot Writes API Documents with Springfox+swagger 2

With the separation of front-end and back-end, excuse documents become particularly important. springfox automatically generates API documents in the form of annotations. With it, restful API can be written easily. swagger is mainly used to show API documents generated by springfox.

Official address: http://springfox.github.io/springfox/

General Principle of Springfox

The general principle of spring fox is that in the process of project startup, spring context is initialized, the framework automatically loads swagger-related bean s into the current context according to configuration, and automatically scans the classes of api documents that may need to be generated in the system, and generates corresponding information caching. If the project MVC control layer uses spring Mvc, all Controller classes will be scanned automatically, and corresponding api documents will be generated according to the methods in these Controller classes.

Spring integration Springfox steps and instructions:

1. Adding Swagger 2 dependencies

    <!-- Swagger -->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.6.1</version>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.6.1</version>
    </dependency>

Adding configuration to application.properties

#Solving the Problem of Chinese Scrambling
banner.charset=UTF-8
server.tomcat.uri-encoding=UTF-8
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true
spring.messages.encoding=UTF-8

#Swagger Configure Properties
sop.swagger.enable=true
sop.swagger.packageScan=com.example
sop.swagger.title=UserController Restfull API
sop.swagger.description=UserController Restfull API
sop.swagger.version=3.0

Create Swagger ConfigProperties Load Configuration Item

package com.example.config;

import java.io.Serializable;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@ConfigurationProperties(prefix = "sop.swagger")
@Component
public class SwaggerConfigProperties implements Serializable {
    /**
     * Whether to open Swagger
     */
    private boolean enable = false;
    /**
     * Packets to be scanned
     */
    private String packageScan;
    /**
     * Title
     */
    private String title;
    /**
     * describe
     */
    private String description;
    /**
     * Version information
     */
    private String version;

    public boolean isEnable() {
        return enable;
    }

    public void setEnable(boolean enable) {
        this.enable = enable;
    }

    public String getPackageScan() {
        return packageScan;
    }

    public void setPackageScan(String packageScan) {
        this.packageScan = packageScan;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }
}

4. Create Swagger2 configuration class

package com.example.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class Swagger2 {
    @Autowired
    private SwaggerConfigProperties scp;

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(apiInfo())
            .select()
            .apis(RequestHandlerSelectors.basePackage("com.example.web"))
            .paths(PathSelectors.any())
            .build();
    }

    private ApiInfo apiInfo() {

        return new ApiInfoBuilder()
            .title(scp.getTitle())
            .description(scp.getDescription())
            .version("1.0")
            .build();
    }
}

5. Creating model s

package com.example.model;

import io.swagger.annotations.ApiModelProperty;

public class User {
    @ApiModelProperty(value = "Primary key")
    private Long id;
    @ApiModelProperty(value = "Name")
    private String name;
    @ApiModelProperty(value = "Age")
    private Integer age;
    @ApiModelProperty(value = "Password")
    private String password;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
            "id=" + id +
            ", name='" + name + '\'' +
            ", age=" + age +
            ", password=" + password +
            '}';
    }
}

6. Creating Controller

package com.example.web;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import com.example.model.User;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;

@RestController
@Api("userController Relevant api")
public class UserController {
    @ApiOperation("Getting User Information")
    @ApiImplicitParams({
        @ApiImplicitParam(paramType = "header", name = "username", dataType = "String", required = true,
            value = "User's Name", defaultValue = "xiaoqiang"),
        @ApiImplicitParam(paramType = "query", name = "password", dataType = "String", required = true, value = "User's password",
            defaultValue = "xiaoxiong")
    })
    @ApiResponses({
        @ApiResponse(code = 400, message = "Request parameters not filled in"),
        @ApiResponse(code = 404, message = "No request path or incorrect page Jump path")
    })
    @RequestMapping(value = "/getUser", method = RequestMethod.GET)
    public User getUser(@RequestHeader("username") String username, @RequestParam("password") String password) {
        User user = new User();
        user.setName(username);
        user.setPassword(password);
        return user;
    }

    @ApiOperation(value = "Creating Users", notes = "according to User Object Creation User")
    @ApiImplicitParam(name = "user", value = "User Detailed Entities user", required = true, dataType = "User")
    @RequestMapping(value = "", method = RequestMethod.POST)
    public String postUser(@RequestBody User user) {
        Map<Long, User> users = Collections.synchronizedMap(new HashMap<Long, User>());
        users.put(user.getId(), user);
        return "success";
    }

    @ApiIgnore
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String home() {
        return "hello";
    }
}

Complete the above code addition, start Spring Boot program, access: http://localhost:8080/swagger-ui.html
. You can see the page of the RESTful API shown earlier. We can then click on specific API requests and take POST type / users requests as an example to find the Notes information we configure in the above code and the description information of the parameter user, as shown in the following figure.

7. API Document Access and Debugging

In the page requested above, we see that user's Value is an input box. Yes, Swagger provides debugging and testing as well as interface functions. We can click Model Schema on the right side of the figure above (yellow area: it indicates the data structure of User). At this time, there is a template for user object in Value. We just need to modify it slightly and click "Try it out!" Button, you can complete a request call!

At this point, you can also verify the correctness of previous POST requests by several GET requests.

Compared with the work of documenting these interfaces, our added configuration content is very small and concise, and the intrusion of the original code is also tolerated. Therefore, while building RESTful API, adding swagger to manage API documents is a good choice.

8. Introduction of some parameters of spring fox, swagger.annotations annotations

The above only shows how to use the swagger annotations. Here we will explain the swagger annotations added above. When using the notes, we refer to the swagger annotations Api manual. Next, we will introduce some of the commonly used annotations.

  • @ ApiIgnore ignores annotated classes or methods and does not add them to API documents
  • @ ApiOperation presents basic information for each API
    • value api name
    • notes Notes
  • @ ApiImplicitParam is used to specify the type, name, whether or not the receiving parameters must be received, and so on.
    • Receiving parameter name in name correspondence method
    • value Notes
    • Do required have to boolean
    • One of the paramType parameter types body, path, query, header, form
    • body Receiving data using @RequestBody POST Effective
    • path configures {} parameters in url
    • Query common query parameters such as? Query = q, jQuery ajax data set values can also be, for example{query:"q ”} Spring MVC does not need to add annotation reception
    • header Receiving data using @RequestHeader
    • form Author Not Used, Please Check Official API Documents
    • DataType data type. If the type name is the same, specify the full path, such as dataType = java.util.Date, and springfox automatically generates the model based on the type.
  • @ApiImplicitParams Contains multiple @ApiImplicitParam
  • @ ApiModelProperty adds descriptions of attributes in the model, such as PageInfoBeen and BlogArticleBeen above, which can only be used in classes.
  • value parameter name
  • Do required have to boolean
  • Does hidden hide boolean?
  • Other information and the same name attribute above work the same way, hidden attributes can not be hidden for collections, and the reason is not known at present.
  • @ ApiParam describes a single parameter, either in a class or in a controller method. The attributes in the annotations work the same as the same-name attributes listed above.

Other notes: https://github.com/swagger-api/swagger-core/wiki/Annotations#apimodel

9. The pits in spring Fox

springfox first pit: Controller class parameters, pay attention to prevent infinite recursion.

Spring mvc has a powerful parameter binding mechanism, which can automatically bind request parameters to a custom command object. So, when many developers write Controller, in order to be lazy, they directly take an entity object as a parameter of the Controller method. For example, the following sample code:
@RequestMapping(value = "update")
public String update(MenuVo menuVo, Model model){
}
This is the code that most programmers like to write in Controller to modify an entity. When integrating with swagger, there is a big pit. If all the attributes in the MenuVo class are primitive types, that's fine. There's no problem. But if there are some other attributes of custom types in this class, and this attribute directly or indirectly exists attributes of its own type, then there will be problems. For example, if the MenuVo class is a menu class, it contains an attribute parent of the MenuVo type to represent its parent menu. In this way, swagger module can not load the api at system startup and directly report an error. The reason for the error is that the parameters of the update method are parsed during loading the method. It is found that the parameter MenuVo is not a simple type, and all its class attributes are automatically interpreted recursively. In this way, it is easy to fall into an infinite recursive dead cycle.

To solve this problem, I have just written an OperationParameterReader plug-in implementation class and the ModelAttributeParameterExpander tool class it relies on, replacing the original two classes of srpingfox by configuration, replacing the logic of parameter parsing by stealing beams and columns, and avoiding infinity. Recursion. Of course, this is equivalent to a way of modifying the source level. I haven't found a better way to solve this problem yet, so I can only recommend that you try to avoid this infinite recursion when using spring-fox Swagger. After all, this does not conform to the spring MVC command object specification. The spring MVC parameter command object has only simple basic type attributes.

springfox's second biggest pit: api grouping is relevant, and Docket instances cannot be lazily loaded

springfox defaults to grouping all APIs so that when accessed through addresses like http://127.0.0.1:8080/jadDemo/swagger-ui.html, all api lists are loaded on the same page. In this way, if the system is slightly larger and the api is slightly more, the page will appear false death, so it is necessary to group the apis. The api grouping is done through the ApiConf configuration file. Define some Docket instances with the @Bean annotation The common configurations on the Internet are as follows:

@EnableWebMvc
@EnableSwagger2
public class ApiConfig {
@Bean
 public Docket customDocket() {
       return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo());
    }
}

In the above code, a Docket is injected through @Bean This configuration is not required. Without this configuration, the framework generates a default Dock instance itself. The purpose of this Docket instance is to specify the public information of all APIs it can manage, such as the api version, author, and other basic information, and to specify which APIs are listed only (filtered through api addresses or annotations).
Docket instances can be multiple, such as the following code:

@EnableWebMvc
@EnableSwagger2
public class ApiConfig {
@Bean
 public Docket customDocket1() {
       return new Docket(DocumentationType.SWAGGER_2)
.groupName("apiGroup1").apiInfo(apiInfo()).select()
.paths(PathSelectors.ant("/sys/**"));
    }
@Bean
 public Docket customDocket2() {
       return new Docket(DocumentationType.SWAGGER_2)
.groupName("apiGroup2").apiInfo(apiInfo())
.select()
.paths(PathSelectors.ant("/shop/**"));
    }
}

When multiple Docket instances are configured in the project, the APIs can be grouped, for example, the code above divides the APIs into two groups. In this case, each group must be given a different name, such as "apiGroup1" and "apiGroup2" in the code above, and each group can use paths to specify which group manages which APIs through an ant-style address expression. For example, in the above configuration, the first set of management addresses is / sys / at the beginning of the api, and the second set of management / shop / at the beginning of the api. Of course, there are many other filtering methods, such as following class annotations, method annotations, address regular expressions and so on. After grouping, different API groups can be selected in the drop-down option in the upper right corner of the API list interface. This decentralizes the API list of the project to different pages. In this way, it is easy to manage, and it is not likely that the page will die because it needs to load too many apis.
However, Same as using @ConfigurationI'm not in favor of using @Bean to configure Docket instances to group APIs . Because in this way, the code will also be written to death. So I recommend configuring Docket instances in XML files to implement these similar functions. Of course, considering the many attributes in Docket, it is difficult to configure beans directly. You can write a FactoryBean for Docket and configure FactoryBean in the XML file. However, when configuring Docket into xml. Another big pit is that spring loads beans late by default, after configuring these Docket instance beans directly in xml. You will find that, without any effect, there are no grouping items in the drop-down list in the upper left corner of the page.
This problem has been bothering me for hours, and then it was speculated from experience that it might be due to the default delayed loading of sping bean s, an instance of Docket that has not yet been loaded into spring context. Facts have proved that my guess is correct. I don't know if this is a bug in springfox, or because I shouldn't have moved the configuration of Docket from the original java code to the xml configuration file.

springfox other pits:

springfox has other pits. For example, in the @ApiOperation annotation If the httpMethod attribute is not specified as a get or post method, all the methods in the api list, such as get,post,delete,put, are listed. It is ugly that the api list repeats too much. In addition, there are also problems with login permissions when testing, and so on. This pile of pits is relatively easy to solve, because the space is limited, I will not say more. And for example @Api,@Usage of annotations such as ApiOperation and @ApiParam I won't repeat many documents on the internet.

Reference to some of the contents: http://m.w2bc.com/article/229092

Keywords: Java Spring encoding xml

Added by hostcord on Tue, 02 Jul 2019 00:05:06 +0300