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

前言

网上关于 Spring Boot 中使用 Swagger 3 的有效文章真的很少,百度出来的基本全是 Swagger 2.X 的内容,或者打着 Swagger 3 的旗号,写着 2.X 的内容。因此我觉得有必要记录我在使用 Swagger 3(OpenAPI 3.0)的过程中的一些事项,希望能帮助到更多的人。

示例代码已上传到 GitHub:spring-boot-springfox-swagger3

OpenAPI 与 Swagger

OpenAPI 简介

OpenAPI 规范(OAS)为 RESTful API 定义了一个标准、与编程语言无关的 API 描述规范。它允许人类和计算机在不查看源码、文档或通过监控网络流量的情况下发现和理解服务。正确使用 OpenAPI ,可以让使用者在不需要或更少的理解服务实现逻辑情况下,更好更轻松的理解服务并与之交互。

完整的 OpenAPI 规范请看:OpenAPI Specification

Swagger 简介

上述说到 OpenAPI 是一个规范,而 Swagger 也是一个规范,是 OpenAPI 规范的前身。Swagger 规范已于 2015 年捐赠给 Linux 基金会后改名为 OpenAPI,并定义最新的规范为 OpenAPI 3.0。所以现在的 Swagger 3.0 就是 OpenAPI 3.0。

通常我们所说的 Swagger 是指由 SmartBear Software 开发维护的一套用于实现 OpenAPI 规范的工具组合名称。Swagger 工具包含开源、免费和商用工具的组合,可用于整个 API 生命周期的开发。

关于这两者区别的详细信息可查看:What Is the Difference Between Swagger and OpenAPI?

Swagger 官方:https://swagger.io/

一句话总结:OpenAPI 是一个规范,而 Swagger 是用于实现规范的工具组合。

SpringFox 与 SpringDoc

SpringFox 简介

SpringFox 是 Spring 社区维护的一个项目(非官方),帮助使用者将 Swagger 2 集成到 Spring 中。常常用于 Spring 中帮助开发者生成文档,并可以轻松的在 Spring Boot 中使用。

SpringFox 3.0 相关特性

  • 支持 Spring 5,Webflux(仅请求映射支持,尚不支持功能端点)、Spring Integration
  • 补充官方在 Spring Boot 的自动装配 springfox-boot-starter, 以后只需依赖一个 dependency
  • 与2.0更好的规范兼容性
  • 支持OpenApi 3.0.3
  • 轻依赖 spring-pluginswagger-core
  • 现有的 Swagger 2 注解继续有效并丰富 OpenAPI 3.0 规范

Github: https://github.com/springfox/springfox

SpringDoc 简介

SpringDoc 也是 Spring 社区维护的一个项目(非官方),帮助使用者将 Swagger 3 集成到 Spring 中。

SpringDoc 支持 Swagger 页面 Oauth2 登录,相较于 SpringFox 而言,它的支撑时间更长,无疑是更好的选择。但是在国内发展较慢,很少看到太多有用的文档,百度出来的基本全是 Swagger 2 的内容,不过 官网文档 也给出了详细的使用说明。SpringDoc 使用了 Swagger 3(OpenAPI 3),但 Swagger 3 并未对 Swagger 2 的注解做兼容,不易迁移。

从 SpringFox 迁移到 SpringDoc

官网文档有介绍:Migrating from SpringFox

这里就不做过多介绍了。

以下将介绍使用 Springfox 来集成 Swagger 3 到 Spring Boot 中,并使用 Swagger 3 的注解。

关于使用 SpringDoc 来集成 Swagger 3(OpenAPI 3)的介绍请看我的另一篇文章:xxxx

Spring Boot 中使用 Springfox 整合 Swagger 3(OpenAPI 3)

Swagger 3(OpenAPI 3)常用注解介绍

1. @OpenAPIDefinition

用于描述标签、文档信息、安全配置及外部文档,用在类上。

常用参数:

  • info:描述文档信息,详情见以下的 @Info
  • tags:定义标签列表,详情见以下的 @Tag
  • servers:目标服务器连接列表,详情见以下的 @Server
  • externalDocs:API 的一些外部文档,详情见以下的 @ExternalDocumentation

示例:

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 {
}

2. @Info

用于说明文档信息,用在 @OpenAPIDefinition 中。

常用参数:

  • title:应用标题
  • description:应用描述
  • contact:联系信息,详情看 @Contact
  • license:许可信息,详情看 @License
  • version:版本

示例:

1
2
3
4
5
6
7
8
9
10
11
12
@OpenAPIDefinition(
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")
)
)
@Configuration
public class SwaggerConfig {
}

3. @Tag

对一个 operation 进行说明或定义的标签,用在类或方法上,也可以用在 @OpenAPIDefinition 中定义标签。

常用参数:

  • name:名称
  • description:描述

示例:

用在类上:

1
2
3
4
@Tag(name = "用户管理")
@RestController
public class UserController {
}

用在 @OpenAPIDefinition 中定义标签:

1
2
3
4
5
6
7
8
9
@OpenAPIDefinition(
tags = {
@Tag(name = "用户管理", description = "用户模块操作"),
@Tag(name = "角色管理", description = "角色模块操作")
}
)
@Configuration
public class SwaggerConfig {
}

值得注意的是,@Tag 在使用 Springfox 整合时,配置在 Controller 上使用并没有生效,在 Swagger UI 中没有看到相关显示。 这似乎是个 BUG,相关 issues: @Tag annotation not work as expected on the Controller class.

目前还未修复,解决方法是使用 2.X 版本的注解替代:@Api(tags = "用户管理")

这个问题在 Springdoc 中不存在,通过 Springdoc 来生成 API 文档时,@Tag 使用正常,在 Swagger UI 中正常显示。

4. @Contact

用于描述联系人信息,用在 @Info 中。

常用参数:

  • name:唯一名称(个人/组织)
  • url:指向联系人信息的 URL
  • email:邮箱

示例:

1
2
3
4
5
6
7
8
@OpenAPIDefinition(
info = @Info(
contact = @Contact(name = "lanweihong", email = "986310747@qq.com", url = "https://www.lanweihong.com")
)
)
@Configuration
public class SwaggerConfig {
}

5. @License

用于藐视许可证信息,用在 @Info 中。

常用参数:

  • name:许可证名称
  • url:指向许可证的 URL

示例:

1
2
3
4
5
6
7
8
@OpenAPIDefinition(
info = @Info(
license = @License(name = "Apache 2.0", url = "http://www.apache.org/licenses/LICENSE-2.0.html")
)
)
@Configuration
public class SwaggerConfig {
}

6. Server

用于配置目标主机,用在 @OpenAPIDefinition 中。

常用参数:

  • url:主机地址
  • description:主机描述

示例:

1
2
3
4
5
6
7
8
9
@OpenAPIDefinition(
servers = {
@Server(description = "生产环境服务器", url = "https://xxxx.com/api/v1"),
@Server(description = "测试环境服务器", url = "https://test.xxxx.com/api/v1")
}
)
@Configuration
public class SwaggerConfig {
}

7. @Operation

用于说明方法用途,用在方法上。

参数:

  • summary:方法概要,方法的一个简单介绍,建议 120 个字符内
  • description:方法描述,一般是很长的内容
  • hidden:是否隐藏

示例:

1
2
3
4
5
@Operation(summary = "查询用户列表", description = "返回所有用户数据")
@GetMapping("/users")
public String getUseList() {
return "";
}

8. @Parameter

用于说明方法参数,用在方法参数上。

参数:

  • name:指定的参数名
  • in:参数位置,可选 queryheaderpathcookie,默认为空,表示忽略
  • description:参数描述
  • required:是否必填,默认为 false

示例:

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

9. @ApiResponse

用于说明一个响应信息,用在 @ApiResponses 中。

参数:

  • responseCode:HTTP 响应码
  • description:描述

示例:

1
2
3
4
5
6
7
8
9
10
@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();
}

10. @ApiResponses

用于说明一组响应信息,比如一个请求可能返回多种响应情况,比如成功信息(200),也有可能抛参数异常(400),用在方法上。

参数:

  • value@ApiResponse 数组

示例参考以上的 @ApiResponse

11. @Schema

用于描述数据对象信息或数据对象属性,比如各种 POJO 类及属性,用在类或类属性上。

参数:

  • name:属性名称
  • description:属性描述
  • required:是否必须
  • minLength:字符最小长度
  • maxLength:字符最大长度

示例:

1
2
3
4
5
6
7
8
9
10
11
@Schema(description = "用户实体")
public class UserVO {

@Schema(description = "用户名")
private String userName;

@Schema(description = "邮箱")
private String email;

// Getter And Setter ...
}

值得注意的是,当使用 Springfox 整合时, @Schema 配置在类上时在 Swagger UI 中并未生成其配置的信息,但是配置在类属性上却是正常的。配置在类上时可使用 @ApiModel(description = "用户参数对象") 来替代。

这个问题在使用 Springdoc 整合时不存在,在 Springdox 整合使用时,@Schema 配置在类和属性上均正常。

12. @Hidden

用于隐藏资源、类或属性,用在类、方法或属性上。

示例:

1
2
3
4
5
6
7
8
9
10
11
@RestController
@RequestMapping("/api/roles")
// 隐藏整个 RoleController
@Hidden
public class RoleController {

@GetMapping("")
public JsonResult<String> queryRoleList() {
return JsonResult.ok();
}
}

一般常用的也就这几个注解,若想要了解更多的注解,请参阅 OpenAPI Specification

以下使用教程使用 Springfox 整合,可能对新注解支持不是特别好,有些注解添加了也不生效,因此以下配置中的代码并非全用到新注解。所以,我还是推荐使用 Springdoc 来整合生成文档。

关于使用 Springdoc 来整合 OpenAPI 3 生成文档的介绍,请看我的另一篇文章:Spring Boot 中使用 SpringDoc 整合 Swagger 3(OpenAPI 3)生成 API 文档

整合 Swagger 3(OpenAPI 3)

添加依赖

编辑 pom.xml,添加依赖:

1
2
3
4
5
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>

添加 Swagger 配置类

  1. 添加 Swagger 属性配置类,用于封装 Swagger 配置参数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @ConfigurationProperties(prefix = "spring.swagger")
    @Data
    public class SwaggerProperties {

    private Boolean enable;
    private String groupName;
    private String basePackage;
    private String version;
    private String title;
    private String description;
    private String contactName;
    private String contactEmail;
    private String contactUrl;
    }
  2. 添加 Swagger 配置类,添加注解 @EnableOpenApi

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
@Configuration
@EnableOpenApi
@EnableConfigurationProperties(value = {SwaggerProperties.class})
public class SwaggerConfig {

SwaggerProperties swaggerProperties;

@Autowired
public void setSwaggerProperties(SwaggerProperties swaggerProperties) {
this.swaggerProperties = swaggerProperties;
}

/**
* API
* @return
*/
@Bean
public Docket adminApi() {
// OAS_30:区别于 V2,(OpenAPI Specification 的简称 OAS)
return new Docket(
// 使用 OpenAPI 3.0
DocumentationType.OAS_30)
.enable(swaggerProperties.getEnable())
// API 信息
.apiInfo(getAdminApiInfo())
// API 分组
.groupName(swaggerProperties.getGroupName())
.select()
// 对某个包的接口进行监听
.apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()))
// 监听所有接口
// .apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}

/**
* API 信息
* @return
*/
private ApiInfo getAdminApiInfo() {
return new ApiInfoBuilder()
// 文档标题
.title(swaggerProperties.getTitle())
// 文档描述
.description(swaggerProperties.getDescription())
// 联系人信息
.contact(new Contact(swaggerProperties.getContactName(), swaggerProperties.getContactUrl(), swaggerProperties.getContactEmail()))
// 文档版本
.version(swaggerProperties.getVersion())
.build();
}
}

使用 Swagger 3

  1. 添加 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
    54
    55
    @RestController
    @RequestMapping("/api/users")
    // @Tag 注解不生效,似乎是 BUG,相关 issues:https://github.com/springfox/springfox/issues/3668,因此这里使用 2.X 的注解 @Api
    // @Tag(name = "用户管理", description = "用户数据增删改查")
    @Api(tags = "用户管理")
    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<>();
    result.add(new UserVO("zhangsan", "zhangsan@lanweihong.com"));
    result.add(new UserVO("lisi", "lisi@lanweihong.com"));
    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();
    }

    @Hidden
    @PostMapping("/test")
    public JsonResult<UserVO> testAddUser(@Valid @RequestBody UserParam userParam) {
    // TODO
    return JsonResult.ok();
    }
    }
  2. 添加 VO:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    @Getter
    @Setter
    // @Schema 注解用在类上不生效,使用 @ApiModel 替代
    @ApiModel(description = "用户实体")
    @ToString
    public class UserVO {

    public UserVO() {

    }

    public UserVO(String userName, String email) {
    this.userName = userName;
    this.email = email;
    }

    @Schema(name = "用户名", description = "用户名")
    private String userName;

    @Schema(description = "邮箱")
    private String email;
    }
  3. 编辑项目 application.yml,添加以下参数:

1
2
3
4
5
6
7
8
9
10
11
12
# 自定义 Swagger 配置
spring:
swagger:
enable: true
groupName: 前端
basePackage: com.lanweihong.springfox.swagger
version: 1.0.0
title: 前端
description: 开发文档
contactName: lanweihong
contactEmail: 986310747@qq.com
contactUrl: blog.lanweihong.com
  1. 项目中如果使用了 Spring Security 的,要添加以下配置放行 Swagger UI 的相关资源:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Configuration
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/v3/api-docs",
    "/v3/api-docs/**",
    "/swagger-resources/**",
    "/swagger-ui/**");
    }
  2. 启动项目进行测试。

启动项目,浏览器访问:http://localhost:8080/swagger-ui/注意:Swagger 2.X 版本访问地址是 http://localhost:8080/swagger-ui.html,这和 3.0 不一样。),可看到 Swagger UI 界面:

主页面:

主页面

查看 GET 请求:

查看 GET 请求

查看 POST 请求:

查看 POST 请求

实体类描述:

实体类描述

至此,Spring Boot 中使用 SpringFox 整合 Swagger 3 的使用已完成。

示例代码已上传到 GitHub:spring-boot-springfox-swagger3

参考文档

  1. what-is-the-difference-between-swagger-and-openapi
  2. Springfox Reference Documentation
  3. Swagger3 注解使用(Open API 3)