Spring Boot 中使用 SpringDoc 整合 Swagger 3(OpenAPI 3)生成 API 文档

前言

关于 Swagger 和 OpenAPI 的介绍,请看我的另一篇文章:Spring Boot 中使用 SpringFox 整合 Swagger 3(OpenAPI 3)生成 API 文档 ,文章有介绍 Swagger 和 OpenAPI 和 Swagger 3(OpenAPI 3)的新注解介绍及使用方法,也建议你阅读本文前先去看看。

本文仅介绍在 Spring Boot 中使用 Springdoc 整合 Swagger 3.0(OpenAPI 3)。

示例代码已上传到 Github:spring-boot-springdoc-swagger3

springdoc-openapi 介绍

springdoc-openapi java 库可助于我们在 Spring Boot 项目自动生成 API 文档。springdoc-openapi 通过在运行时检查应用程序来根据 Spring 配置、类结构和各种注释推断 API 语义,自动生成 JSON/YAML 和 HTML 格式的 API 文档。

springdoc-openapi 支持:

  • OpenAPI 3
  • Spring Boot (v1 and v2)
  • 支持 JSR-303 校验规范,如 @NotNull, @Min, @Max, @Size 等注解
  • swagger-ui
  • OAuth 2
  • GraalVM 原生镜像

Swagger 3.0(Open API 3.0)注解介绍

关于注解的介绍请看我的另一篇文章 Spring Boot 中使用 SpringFox 整合 Swagger 3(OpenAPI 3)生成 API 文档 ,里面有详细的介绍和示例代码,这里就不过多赘述了。

使用

添加依赖

编辑 pom.xml 文件,添加依赖:

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

这将自动把 swagger-ui 部署到 Spring Boot 应用中:

  • 文档将以 HTML 格式展示,使用官方 swagger-ui jars
  • Swagger UI 页面将通过 http://server:port/context-path/swagger-ui.html 访问
  • OpenAPI 描述将通过 http://server:port/context-path/v3/api-docs 访问,提供 JSON 格式
  • 文档提供 yaml 格式,位于以下路径:/v3/api-docs.yaml

启动项目后,使用浏览器访问:http://localhost/swagger-ui.html 就能看到 Swagger UI 展示的 API 文档。

swagger-ui

添加 Swagger 配置

新增配置类 SwaggerConfig,用于配置全局 API 文档信息:

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
@OpenAPIDefinition(
tags = {
@Tag(name = "用户管理", description = "用户模块操作"),
@Tag(name = "角色管理", description = "角色模块操作")
},
info = @Info(
title = "用户接口 API 文档",
description = "用户数据管理......",
version = "1.0.0",
contact = @Contact(name = "lanweihong", email = "986310747@qq.com", url = "https://www.lanweihong.com"),
license = @License(name = "Apache 2.0", url = "http://www.apache.org/licenses/LICENSE-2.0.html")
),
servers = {
@Server(description = "生产环境服务器", url = "https://xxxx.com/api/v1"),
@Server(description = "测试环境服务器", url = "https://test.xxxx.com/api/v1")
},
security = @SecurityRequirement(name = "Oauth2"),
externalDocs = @ExternalDocumentation(
description = "项目编译部署说明",
url = "http://localhost/deploy/README.md"
)
)
@Configuration
public class SwaggerConfig {
}

添加 Controller

编写 Controller:

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
@Tag(name = "用户管理", description = "用户数据增删改查")
@RestController
@RequestMapping("/api/users")
public class UserController {

@Operation(summary = "查询用户列表", description = "返回所有用户数据")
@GetMapping("")
public JsonResult<List<UserVO>> getUserList(@Parameter(description = "用户名") @RequestParam(value = "username", required = false) String userName) {
List<UserVO> result = new ArrayList<>();
// TODO
return JsonResult.ok(result);
}

@Operation(summary = "通过用户名查询用户", description = "根据用户名查询用户详细信息")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "请求成功"),
@ApiResponse(responseCode = "404", description = "用户不存在", content = @Content)
})
@GetMapping("/{username}")
public JsonResult<UserVO> getUserByName(@Parameter(description = "用户名", required = true) @PathVariable("username") String userName) {
// TODO
return JsonResult.ok();
}

@Operation(summary = "新增用户")
@PostMapping("")
public JsonResult<UserVO> addUser(@Parameter(required = true) @Valid @RequestBody UserParam param) {
// TODO
return JsonResult.ok();
}

@Operation(summary = "修改用户")
@PutMapping("")
public JsonResult<UserVO> updateUser(@Parameter(description = "用户参数", required = true) @Valid @RequestBody UserParam param) {
// TODO
return JsonResult.ok();
}

@Operation(summary = "删除用户")
@DeleteMapping("/{username}")
public JsonResult<UserVO> deleteUserByName(@Parameter(name = "username", in = ParameterIn.PATH, description = "用户名", required = true) @PathVariable("username") String userName) {
// TODO
return JsonResult.ok();
}

@Operation(summary = "测试新增用户接口", hidden = true)
@PostMapping("/test")
@Hidden
public JsonResult<UserVO> testAddUser(@Parameter(required = true) @Valid @RequestBody UserParam param) {
// TODO
return JsonResult.ok();
}
}

添加相关 POJO

  1. 添加 UserParam,用于接收前端参数:
    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
    @Getter
    @Setter
    @Schema(description = "用户参数实体")
    public class UserParam {

    @NotBlank(message = "用户名不能为空")
    @Schema(description = "用户名")
    private String userName;

    @Schema(name = "roles", description = "角色id列表")
    @NotNull(message = "角色id列表不能为空")
    @Size(min = 1)
    private Set<String> roleList;

    @NotBlank(message = "密码不能为空")
    @Schema(description = "密码,6-18位,包含大小写、数字及特殊字符")
    @Size(min = 6, max = 18)
    private String password;

    @Schema(example = "zhangsan@lanweihong.com", description = "邮箱")
    private String email;

    @Schema(description = "年龄")
    @Min(value = 1, message = "最小年龄为1")
    @Max(value = 150, message = "最大年龄为150")
    private Integer age;
    }

这里 UserParam 使用 JSR-303 Bean Validation 注解来校验参数,springdoc-openapi 会帮我们生成相应的说明信息,如下图:

Swagger UI中的说明信息

全局异常处理

新增全局异常处理类 GlobalExceptionHandler

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
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

@ExceptionHandler(InvalidParameterException.class)
@ResponseStatus(code = HttpStatus.BAD_REQUEST)
public JsonResult<String> bindInvalidParameterExceptionHandler(InvalidParameterException e) {
log.error("错误的请求,参数无效:", e);
return JsonResult.error(e.getMessage());
}

@ResponseStatus(code = HttpStatus.BAD_REQUEST)
@ExceptionHandler(value = BadRequestException.class)
public JsonResult<String> bindBadRequestExceptionHandler(BadRequestException e) {
log.error("错误的请求:", e);
return JsonResult.error(e.getError());
}

@ResponseStatus(code = HttpStatus.NOT_FOUND)
@ExceptionHandler(value = NotFoundException.class)
public JsonResult<String> bindNotFoundExceptionHandler(NotFoundException e) {
log.error("资源未找到:", e);
return JsonResult.error(e.getError());
}

// 其他异常处理......
}

分组

  1. 可以在通过配置 application.yml 来设置分组:
1
2
3
4
5
6
7
8
9
10
11
12
springdoc:
# 分组配置
group-configs:
- group: All
packagesToScan: com.lanweihong.springdoc.swagger.controller
pathsToMatch: /api/**
- group: User
packagesToScan: com.lanweihong.springdoc.swagger.controller.user
pathsToMatch: /api/users/**
- group: Role
packagesToScan: com.lanweihong.springdoc.swagger.controller.role
pathsToMatch: /api/roles/**
  1. 也可以在配置类中添加 GroupedOpenApi
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @Configuration
    public class SwaggerConfig {

    @Bean
    public GroupedOpenApi userApi() {
    return GroupedOpenApi.builder()
    .group("User")
    .pathsToMatch("/api/users/**")
    .build();
    }

    @Bean
    public GroupedOpenApi roleApi() {
    return GroupedOpenApi.builder()
    .group("Role")
    .pathsToMatch("/api/roles/**")
    .build();
    }
    }

注意:这两个方法只能选择一种来配置,比如在 application.yml 配置了就不要再通过代码添加 GroupedOpenApi ,否则会报错,除非你设置的 group 不一样。

  1. 为特定组设置自定义的 API 文档信息

在以上的 SwaggerConfig 配置类中,我们使用 @OpenAPIDefinition 来设置全局的 API 文档信息。但有时,我们想切换到某个组(group)的时候,API 文档信息显示关于这个组的相关信息,而显示全局的信息。这时,我们可以定义一个 OpenApiCustomiser 添加到 GroupedOpenApi 中:

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
@Configuration
public class SwaggerConfig {

@Bean
public GroupedOpenApi userApi() {
return GroupedOpenApi.builder()
.group("User")
// 指定路径
.pathsToMatch("/api/users/**")
// 指定特定的 API 文档信息
.addOpenApiCustomiser(userApiCustomiser())
.build();
}

/**
* 定义 OpenApiCustomiser ,用于指定的 group
* @return
*/
public OpenApiCustomiser userApiCustomiser() {
return openApi -> openApi.info(new io.swagger.v3.oas.models.info.Info()
.title("用户管理 API 文档")
.description("实现对用户数据的增删改查等操作")
.version("1.0")
.contact(new io.swagger.v3.oas.models.info.Contact().name("lanweihong").email("986310747@qq.com")));
}
}

在 Swagger UI 中看到 User 组的文档信息已更新显示,不再是全局的文档信息。

用户分组 API 文档信息

测试

启动项目,访问 http://localhost:8080/swagger-ui.html 访问 Swagger UI。

主页面:

主页面

请求接口描述:

请求描述

分组:

分组

实体类描述:

实体类描述

至此,Spring Boot 中使用 Springdoc 整合Swagger 3(OpenAPI 3)的介绍已完成。

总结

本文仅介绍常用的注解用法及配置,想要了解更多的用法请查看官方文档:OpenAPI 3 Library for spring-boot

使用到的代码已上传到 Github:spring-boot-springdoc-swagger3

参考文档

  1. OpenAPI 3 Library for spring-boot
  2. Swagger3 注解使用(Open API 3)