使用 OpenAPI3 快速生成 REST API 文档

Posted by mingfer on July 7, 2020

springdoc-openapi 是一个在 Spring Boot 项目中快速生成 API 文档的 Java 库。springdoc-openapi 通过在运行时检查检查程序中的 Spring 配置,类的定义和各种注解来推导 API 的语义。

集成

默认的情况下,我们需要先将 springdoc-openapi 添加到项目依赖列表中:

1
2
3
4
5
<dependency>
  <groupId>org.springdoc</groupId>
  <artifactId>springdoc-openapi-ui</artifactId>
  <version>1.4.3</version>
</dependency>

然后启动应用,打开地址 http://server:port/swagger-ui.html 就可以访问到 API 接口的 UI 页面了。

配置

springdoc-openapi 支持通过 Spring Boot 的 Bean 对文档的一些元数据进行配置。下面是两个常用的配置,这些 Bean 使用 @Bean 注解声明在任何一个 @Configuration 注解的配置类中即可:

使用 GroupedOpenApi 对 API 进行分组,防止所有 API 生成在一个页面中,导致阅读体验很差。

1
2
3
4
5
6
7
8
9
@Bean
public GroupedOpenApi actuatorApi() {
    return GroupedOpenApi.builder()
            .group("Actuator")
            .pathsToMatch("/actuator/**")
            .pathsToExclude("/actuator/health/*")
            .build();
}

使用 OpenAPI 配置 API 的元数据信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Bean
public OpenAPI customOpenAPI(BuildProperties buildProperties) {
    return new OpenAPI()
            .components(new Components()
                .addSecuritySchemes("basicScheme", new SecurityScheme()
                    .type(SecurityScheme.Type.HTTP).scheme("basic")))
            .info(new Info()
                .title("Petstore API")
                .version(buildProperties.getVersion())
                .description(
                    "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.")
                    .termsOfService("http://swagger.io/terms/")
                    .license(new License().name("Apache 2.0").url("http://springdoc.org")));
}

注意 BuildProperties 进行下面的配置才能生成对于的配置文件 build-info.properties,在 Spring Boot 中该文件的默认地址是 classpath:META-INF/build-info.properties

Maven:

1
2
3
4
5
6
7
8
9
10
11
12
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <executions>
        <execution>
            <id>build-info</id>
            <goals>
                <goal>build-info</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Gradle:

1
2
3
springBoot {
    buildInfo()
}

使用注解声明 API

这里通过一个 Controller 类,一个 UserEntity 来演示如何通过注解完成文档的编写。代码的逻辑很诡异,但这个不是重点,重点是如何使用 Swagger3 的相关注解声明 API 接口。顺带提一下,这些注解均是遵循规范 OpenAPI-Specification 实现的,所以我们可以通过阅读该规范来了解如何使用这些注解。

UserController.java

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
package cn.mingfer.demo;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("user")
@Tag(name = "user", description = "用户信息接口")
public class UserController {


    @PostMapping("test")
    @Operation(description = "用户信息测试")
    @Parameters({
            @Parameter(name = "uid", description = "用户 ID", required = true, in = ParameterIn.PATH),
            @Parameter(name = "name", description = "用户名称", required = true)
    })
    @ApiResponses({
            @ApiResponse(responseCode = "200", description = "操作成功", content = {
                    @Content(mediaType = "application/json", schema = @Schema(implementation = UserEntity.class, type = "array"))
            }),
            @ApiResponse(responseCode = "400", description = "参数错误", content = {
                    @Content(mediaType = "application/json", schema = @Schema())
            })
    })
    public List<UserEntity> test(@PathVariable("uid") String uid,
                       @RequestParam("name") String name,
                       @RequestBody UserEntity entity) {
        List<UserEntity> entities = new ArrayList<>();
        entities.add(new UserEntity(uid, name));
        entities.add(entity);
        return entities;
    }

}

UserEntity.java

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
package cn.mingfer.demo;

import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;

@Schema(description = "用户信息实体")
public class UserEntity {

    @Schema(description = "用户 ID", type = "string", required = true, minLength = 4, maxLength = 12, example = "10086")
    private final String uid;

    @Schema(description = "用户名称", required = true, example = "mingfer.cn")
    private final String name;

    public UserEntity(@JsonProperty("uid") String uid,
                      @JsonProperty("name") String name) {
        this.uid = uid;
        this.name = name;
    }

    public String getUid() {
        return uid;
    }

    public String getName() {
        return name;
    }
}