Overview of interface documentation

Swagger is a popular real-time interface document generation tool. Interface document is an essential tool in the current project of separating the front and back ends. Before the development of the front and back ends, the back end should first issue the interface document, and the front end develops the project according to the interface document. After the development of both sides, the joint test is carried out.

So interface documentation is essentially a contract between two parties before development. Typically, interface documentation is classified as offline and real-time. Offline interface document tools: Word (equivalent to not said), YAPI, small yomiji, such as this document requires programmers to write in the above, but also generally have interface test function. Typically, the developer writes the information in the offline interface documentation and then hands it off to the front end for reference. The biggest drawback is that when our interface program changes, we have to go back and maintain the above content, which is troublesome, really troublesome.

Real-time interface documents can automatically generate corresponding interface documents according to our code, the advantage is that when our code changes, the generated interface documents will be automatically updated, we do not need to pay attention to the modification, the master needs to release on time. However, because it is generated automatically according to the code, the biggest disadvantage is that the code is highly intrusive, requiring us to integrate the relevant code to generate interface documents in the project code. There are many solutions for real-time interface documentation, but Swagger is one of the more influential.

SpringBoot integrates Swagger2

Of course, the official website is in English, it looks more troublesome. I suggest you just follow my steps, it’s very simple.

There are two common versions of swagger: swagger2 and Swagger3. The differences between the two are minor, and the main optimizations are for dependencies and annotations. Swagger2 requires the introduction of 2 JAR packages, swagger3 requires only one, there is no big difference in use. Take swagger2 as an example.

2.1 Importing Dependencies

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>
Copy the code

2.2 Importing Configuration

First you need to add an annotation: @enablesWagger2. This annotation can be added to the Boot class of SpringBoot or to a custom configuration class. By adding this annotation, we have enabled Swagger in our project.

We take the second approach and define our own configuration class, which happens to add a Docket configuration as well. A Docket configuration is the configuration of a set of interface documents (a project or a version), such as setting names, contacts, and so on.

In config folder, add a SwaggerConfig class.

@Configuration
@EnableSwagger2
public class SwaggerConfig {


    /** * Set multiple: **@Bean* public Docket appApi() { * * List<Parameter> pars = new ArrayList<>(); * ParameterBuilder token = new ParameterBuilder(); * token.name("token").description(" user token").modelref (new modelRef("string")).parameterType("header").Required (false) * .build(); * pars.add(token.build()); * * return new Docket(DocumentationType.SWAGGER_2).select().paths(regex("/app/.*")).build() * .globalOperationParameters(pars).apiInfo(pdaApiInfo()).useDefaultResponseMessages(false) * .enable(enableSwagger) * .groupName("appApi"); * *} * *@Bean* public Docket adminApi() { * * List<Parameter> pars = new ArrayList<>(); * ParameterBuilder token = new ParameterBuilder(); * token.name("token").description(" user token").modelref (new modelRef("string")).parameterType("header").Required (false) * .build(); * pars.add(token.build()); * return new Docket(DocumentationType.SWAGGER_2).select().paths(regex("/admin/.*")).build() * .globalOperationParameters(pars).apiInfo(pdaApiInfo()).useDefaultResponseMessages(false) * .enable(enableSwagger) * .groupName("adminApi"); * *} * * *@return* /

    @Bean
    public Docket createRestApi(a) {
        return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
                .apis(RequestHandlerSelectors.basePackage("com.lsqingfeng.action.swagger.controller")).paths(PathSelectors.any())
                .build().globalOperationParameters(setHeaderToken());

    }

    private ApiInfo apiInfo(a) {
        return new ApiInfoBuilder().title("action-swagger").description("Swagger of actual combat").termsOfServiceUrl("")
                .version("1.0").build();
    }

    / * * *@Description: Sets global parameter * in swagger document@param
     * @Date: 2020/9/11 10:15
     * @return: java.util.List<springfox.documentation.service.Parameter>
     */

    private List<Parameter> setHeaderToken(a) {
        List<Parameter> pars = new ArrayList<>();
        ParameterBuilder userId = new ParameterBuilder();
        userId.name("token").description("User TOKEN").modelRef(new ModelRef("string")).parameterType("header")
                .required(true).build();
        pars.add(userId.build());
        returnpars; }}Copy the code

This is an example of a configuration that also sets up a setToken method, which means that all interfaces that generate documents must contain a token parameter of type header.

2.3 Add annotations to the Controller

The direct description of our interface documentation is mainly in the Controller layer, such as the function of the interface, the name of the parameter, the name of the return value and so on. We need to annotate these values on Controller by adding corresponding annotations to methods, request parameters, and return parameters so that Swagger can help us generate corresponding interface documentation. This is the intrusion into existing code I mentioned earlier.

Let’s write a case.

First of all, create a VO package to write our request and the corresponding parameters, using JavaBean to define the request and response data structure. Note the appropriate annotation here:

Request:

package com.lsqingfeng.springboot.vo;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

/ * * *@className: SwaggerReqVO
 * @description:
 * @author: sh.Liu
 * @date: 2022-03-22 * /
@Data
@apiModel (" Create Swagger request parameters ")
public class SwaggerReqVO {

    @ApiModelProperty("id")
    private Integer id;

    @ ApiModelProperty (" name ")
    private String name;

    @ ApiModelProperty (" gender ")
    private Integer gender;
}
Copy the code

Response:

package com.lsqingfeng.springboot.vo;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

/ * * *@className: SwaggerResVO
 * @description:
 * @author: sh.Liu
 * @date: the word * / 2022-03-22
@Data
@apiModel (" Create Swagger response result ")
public class SwaggerResVO {

    @ApiModelProperty("id")
    private Integer id;

    @ ApiModelProperty (" name ")
    private String name;

    @ ApiModelProperty (" gender ")
    private Integer gender;

    @ ApiModelProperty (" soft ")
    private String what;
}
Copy the code

The @APIModel and @@APIModelProperty annotations are used to define the name of the entity and the name of the field, respectively, for display when generating interface documents.

Look at the Controller:

package com.lsqingfeng.springboot.controller;

import com.lsqingfeng.springboot.vo.SwaggerReqVO;
import com.lsqingfeng.springboot.vo.SwaggerResVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;

/ * * *@className: SwaggerController
 * @description: Swagger interface test *@author: sh.Liu
 * @date: 7 * / 2022-03-22
@RestController
@RequestMapping("/swagger")
@api (value = "user interface ", tags = {" user interface "})
public class SwaggerController {

    @apiOperation (" New user ")
    @PostMapping("save")
    public String save(@RequestBody SwaggerReqVO req) {
        return "success";
    }

    @GetMapping("getById")
    @apiOperation (" Query users based on conditions ")
    public SwaggerResVO getById(@RequestBody SwaggerResVO req) {
        return newSwaggerResVO(); }}Copy the code

The @API annotation and the @apiOperation annotation are used to annotate the interface group name and interface name, respectively. Now we start the project.

The error was reported.

SpringBoot2.6 is incompatible with Swagger2.9.2. Others say it’s because guava’s version of the package is too low.

I tried them all separately, and the high version of guava replaced the dependency problem.

The main cause of this problem is indeed the high version of SpringBoot. This is fine if you are using SpringBoot2.5.x or earlier.

Spring Boot 2.6.X uses PathPatternMatcher to match paths, and Springfox referenced by Swagger uses path matching based on AntPathMatcher.

So to solve the problem, add configuration, change the springBoot MVC road strength matching mode.

Add a configuration to the springBoot configuration file:

spring.mvc.pathmatch.matching-strategy=ANT_PATH_MATCHER
Copy the code

If the configuration file is in YML format:

Start problem resolution again.

Address: IP: port number /swagger-ui.html

Normally you can see our interface. We’ll talk about the abnormal case in a second. Since we only annotate the user interface, all user interfaces can view the Chinese document directly. The remaining two interfaces, without annotations, are presented in default form.

Click on the interface and we can see the details in the interface

Click Model to see the Chinese description of the field. Click Try It Out to debug the interface directly. At the same time, note that the interface is filled with a token, which is the effect of our previous Settings.

By now, the integration of Swagger is complete, mainly generating documents based on our annotations, and debugging can be called online. During development, we only need to complete the Controller layer request and response, as well as method description and other contents, and then provide them to the front end for reference development.

2.4 [404] Problem solved

Normally, we can follow the above steps to appear the page, but sometimes it may be because the version of springBoot is too high. We input the previous address, and 404 appears, which is mainly caused by the failure to read the page under swagger dependency package in the project. If this problem occurs, we can add a configuration class that implements the WebMvcConfigurer interface and add a method:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/ * *").addResourceLocations("classpath:/static/");
    registry.addResourceHandler("swagger-ui.html")
            .addResourceLocations("classpath:/META-INF/resources/");
    registry.addResourceHandler("/webjars/**")
            .addResourceLocations("classpath:/META-INF/resources/webjars/");
}
Copy the code

The complete code is as follows:

package com.lsqingfeng.springboot.config;

import com.lsqingfeng.springboot.interceptor.TokenInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/ * * *@className: WebMvcConfig
 * @description: * webMvc configuration@author: sh.Liu
 * @date: the 2022-01-13 09:51 * /
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {


    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/ * *").addResourceLocations("classpath:/static/");
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/"); }}Copy the code

At this time it is ok to start!

2.5 replace the UI

The whole process above has been completed, but the generated interface document page, in fact, a lot of people do not like, feel not in line with the habits of the people, so there are some big gods, provide other UI test pages. This page is widely used.

How to change: Just import a dependency package:

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>swagger-bootstrap-ui</artifactId>
    <version>1.9.6</version>
</dependency>
Copy the code

Then add another method to the one we just implemented:

registry.addResourceHandler("doc.html")
                .addResourceLocations("classpath:/META-INF/resources/");
Copy the code

Completion code:

package com.lsqingfeng.springboot.config;

import com.lsqingfeng.springboot.interceptor.TokenInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/ * * *@className: WebMvcConfig
 * @description: * webMvc configuration@author: sh.Liu
 * @date: the 2022-01-13 09:51 * /
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

// @Override
// public void addInterceptors(InterceptorRegistry registry) {
/ / / / interception
// registry.addInterceptor(new TokenInterceptor())
// .addPathPatterns("/**")
// .excludePathPatterns("/login");
/ /}

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/ * *").addResourceLocations("classpath:/static/");
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
        registry.addResourceHandler("doc.html")
                .addResourceLocations("classpath:/META-INF/resources/"); }}Copy the code

Restart project: The access path has changed: ** IP: port number /doc.html**

The page appears. We are looking at our user interface:

This style is actually more intuitive and can be debugged directly. Most Swaggers use this style of documentation.

SpringBoot integrates Swagger3

The above has explained the swagger2 integration mode in detail, while swagger3 integration mode is more concise.

First we introduce dependencies:

<dependency>
  <groupId>io.springfox</groupId>
  <artifactId>springfox-boot-starter</artifactId>
  <version>3.0.0</version>
</dependency>
Copy the code

Then there is the alternative annotation: Swagger2 uses the open annotation: @enablesWagger2

In Swagger3, this annotation is changed to @enableOpenAPI

The configuration class:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

@Configuration
public class SwaggerConfig {
    @Bean
    public Docket createRestApi(a) {
        return new Docket(DocumentationType.OAS_30) / / v2
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.example.swaggerv3.controller")) // Set the scan path.build(); }}Copy the code

Note that the version type is OAS_30, which means swagger3.

OAS is short for OpenAPI Specification, translated into Chinese is OpenAPI Specification.

Localhost :8080/swagger-ui/index.html localhost:8080/swagger-ui/index.html

Swagger3’s original UI style has also undergone some changes:

Swagger3 can also be replaced with UI. Same method as Swagger2.

SwaggerUI interceptor and cross-domain conflict handling

If there is cross-domain processing in our project, and there is an interceptor, then we need to use Swagger, in this case, you should pay attention to the possibility that our interceptor will intercept the page path of Swagger, so that the Swagger page cannot be displayed. When we exclude Swagger’s page in the interceptor, It may also cause cross-domain configuration failures.

Detailed solution can see before I wrote a blog: lsqingfeng.blog.csdn.net/article/det…

Specific solutions are briefly mentioned:

The interceptor:

/** * Interceptor configuration **@author liuShuai
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
 
    @Bean
    public TokenInterceptor tokenInterceptor(a) {
        return new TokenInterceptor();
    }
 
 
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry
                .addInterceptor(tokenInterceptor())
                .addPathPatterns("/ * *")
                .excludePathPatterns("/user/login")
                .excludePathPatterns("/user/downloadExcel")
                .excludePathPatterns("/swagger-resources/**"."/webjars/**"."/v2/**"."/swagger-ui.html/**");
    }
 
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/"); }}Copy the code

Cross-domain configuration:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
 
/ * * *@className: CorsConfig
 * @description:
 * @author: sh.Liu
 * @date: in the 2020-12-02 s when * /
@Configuration
public class CorsConfig {
 
    @Bean
    public CorsFilter corsFilter(a) {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOrigin("*");
        config.setAllowCredentials(true);
        config.addAllowedMethod("*");
        config.addAllowedHeader("*");
        UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
        configSource.registerCorsConfiguration("/ * *", config);
        return newCorsFilter(configSource); }}Copy the code

With these two configurations, they can coexist peacefully.

GitCode: gitcode.net/lsqingfeng/…

Branches: feautre/MybatisPlus

All articles will be first updated in the wechat public account, welcome to pay attention to: a wisp of 82 years of wind

Write at the end

As of this article, the series of study notes on SpringBoot has been updated 16 times, and it’s almost time to say goodbye. Thank you all for your support along the way. These sixteen articles are mainly for SpringBoot actual combat learning, basic few will introduce some primitive concepts. It has also integrated most of the current mainstream frameworks and middleware. If you are not familiar with some of Spring’s lifecycle, initialization processes, classloading principles, and common annotations (because this section is less covered), I recommend you follow the Spring5 series of tutorials I wrote earlier, which introduces the core concepts of Spring IOC and AOP.

This is the end of the tutorial series, and there will probably be no further updates to this series. Next, I might be ready to write a series of tutorials on SpringCloud, focusing on some of the component uses in the Alibaba version. However, as the company is busy recently, the update may be slow. I hope you can support me.

Be grateful, don’t say goodbye, and see you in the next series!