通过前面两篇文章:
掌握了SpringDoc集成与注解技巧后,本文将带您深入Swagger UI的实战操作:从界面布局解析到“Try it out”交互式调试,快速打通API测试全流程
SwaggerUI的主界面?
若未使用增强UI(如Knife4j)且通过SpringDoc集成,Swagger UI的默认访问路径为:
http://localhost:8080/swagger-ui/index.html(Swagger 3.x标准路径)
如有上下文路径,需在端口后追加,如:
http://localhost:8080/your-context/swagger-ui/index.html
主界面如图所示:
本文将分模块解析Swagger UI的核心功能区。为便于新读者理解,部分基础概念将结合界面操作同步回顾。
API 资源地址输入框
Swagger 与 Postman、Apifox 同属标准化 API 调试工具,其核心能力不限于调试当前系统。通过顶部的 Spec URL 输入框(如图),可直接加载任意符合 OpenAPI 规范的远程文档路径(如 https://api.example.com/docs/swagger.json),实现跨系统接口的实时渲染与调试。
此特性多用于以下场景:
第三方接口验证:快速调试外部服务(如支付回调 API)
多环境文档聚合:整合测试/生产环境的接口文档
规范兼容性测试:验证自建文档是否符合 OpenAPI 标准
分组
Tag分组
SpringDoc 的分组机制(Groups)是其对原生 Swagger Tag 的扩展封装。在标准 Swagger 规范中,仅通过 Tag 实现接口分类(通过@Operation(tags = “会员管理”)或者@Tag(name = “会员账户模块”)定义)如下:
//使用@Tag注解在类的上方定义该类是属于哪个Tag分组
@RestController
@RequestMapping("/api/member/account")
@Tag(
// 当前Controller在UI界面所显示的名称
name = "会员账户模块",
// 当前Controller在UI界面的描述
description = "用户管理会员账户相关接口"
)
public class MemberAccountController {
//会员账户列表
@GetMapping("/list")
@Operation(summary = "会员账户列表", description = "会员账户列表")
public String list(
@Parameter(description = "memberAccount", required = true) String memberAccount) {
return "请求成功";
}
}
注意:如果一个接口被多个分组扫描到,那么这个接口会同时出现在这些分组中
我们可以在SpringDoc的配置类中通过@OpenAPIDefinition注解,来定义页面上方的一些基础信息的显示:如下
@Configuration
@OpenAPIDefinition(
info = @Info(
title = "电商平台API",
version = "1.2.0",
description = "核心业务API",
contact = @Contact(name = "技术支持", url = "https://support.example.com"),
license = @License(name = "商业许可")),
// 外部文档
externalDocs = @ExternalDocumentation(
description = "数据库设计文档",
url = "https://docs.example.com/db-schema"
)
)
public class SpringDocConfig {
}
我们还可以通过@OpenAPIDefinition中的servers属性,来定义多个请求基础路径(可以简单理解为多种环境),如下
@Configuration
@OpenAPIDefinition(
servers = {
@Server(url = "http://localhost:8080", description = "开发环境"),
@Server(url = "http://www.default.com:8080", description = "默认网关路由"),
@Server(url = "/demonstrate", description = "演示环境")
},
)
public class SpringDocConfig {
}
@SecurityScheme 的核心作用是声明项目中支持的认证机制类型(如 JWT/OAuth2),而非启用具体接口的认证校验。它本质是认证方案的元数据描述器,通过各种不同的参数参数建立安全模型,如下:
//第一种认证方式OAuth2
@SecurityScheme(
// 通行证名称 (后续在门禁系统识别时使用该名称)
name = "OAuth2",
/**
* 通行证类型 (选择哪种安防体系)
* 当前选择:OAUTH2协议认证体系
* 类似还有:API钥匙串(APIKEY)、基础密码锁(HTTP)等
*/
type = SecuritySchemeType.OAUTH2,
/**
* 发证流程说明
* 这里配置如何获取通行证的具体步骤
* 可以设置多种办证方式(当前选择最安全的授权码模式)
*/
flows = @OAuthFlows(
// 授权码模式 (最常用的安全认证流程)
authorizationCode = @OAuthFlow(
// 办证窗口:用户需要前往此地址提交申请
authorizationUrl = "https://auth.example.com/authorize",
// 领证窗口:凭申请码在此处兑换正式通行证(token)
tokenUrl = "https://auth.example.com/token",
/**
* 通行证权限范围说明
* 就像办公室门禁卡有不同权限级别:
* - 普通员工卡只能进入公共区域
* - 经理卡可进入敏感区域
* 这里定义该通行证能解锁哪些操作
*/
scopes = {
// 读取权限:可查看用户信息(普通权限)
@OAuthScope(name = "user:read", description = "读取用户信息"),
// 写入权限:可修改用户信息(高级权限)
@OAuthScope(name = "user:write", description = "修改用户信息")
}
)
)
)
//第二种认证方式JWT
@SecurityScheme(
name = "JWT",
type = SecuritySchemeType.HTTP,
scheme = "bearer",
bearerFormat = "JWT",
in = SecuritySchemeIn.HEADER
)
//第三种认证方式ApiKey
@SecurityScheme(
name = "ApiKey",
type = SecuritySchemeType.APIKEY,
in = SecuritySchemeIn.HEADER,
description = "服务间通信密钥",
paramName = "X-API-KEY"
)
public class SpringDocConfig {
}
在类的上方使用@SecurityRequirements注解,即可对该类中所有接口开启权限认证
@SecurityRequirements中可以配置多重认证方式,任选其一完成认证即可
@RestController
@RequestMapping("/api/member")
@SecurityRequirements({
@SecurityRequirement(name = "ApiKey"), // 要求1:ApiKey
@SecurityRequirement(name = "OAuth2", scopes = {"user:write"}) // 要求2:OAuth2且具有user:write权限
})
public class MemberController {
//会员列表
@GetMapping("/list")
@Operation(
// 接口摘要说明(简洁描述接口功能)
summary = "会员列表",
// 详细接口描述(可包含具体行为说明)
description = "会员列表"
)
public String list(String memberName,String memberType) {
return "请求成功";
}
}
我们通过给接口上方添加@Operation注解,即可对该接口名称和接口描述进行定义
//会员账户列表
@GetMapping("/list")
@Operation(summary = "会员账户列表", description = "会员账户列表")
public String list( String memberAccount) {
return "请求成功";
}
点击Try it out按钮,即可开始对接口的调试,先填写当前接口所需参数,之后点击Execute即可发送请求
如果要取消对该接口的调试,点击Cancel即可
如果接口的参数不进行任何配置,则按照默认样式进行展示,如下图:
但是这种不进行任何配置的方式对于接口的使用调试人员,并不友好,不知道这个参数什么意思,什么类型,于是我们可以使用一个注解对参数进行配置,如下:
@GetMapping("/list")
@Operation(summary = "会员账户列表", description = "会员账户列表")
public String list(
@Schema(
description = "会员账号", // 参数的详细描述
type = "string", // 参数的数据类型
example = "admin" , // 参数的示例值
name = "memberAccount", // 参数的名称
allowableValues = {"admin", "zhangsan"}, // 参数的可选值(不配置则显示为输入框,随意输入,配置后则显示为下拉框,只能选择输入)
requiredMode = Schema.RequiredMode.REQUIRED // 参数是否必填, REQUIRED-必填,NOT_REQUIRED-非必填,AUTO自动推断
)
String memberAccount
) {
return "请求成功";
}
如果一个接口参数是一个文件类型的参数(例如文件上传接口),该怎样配置呢?
@PostMapping("/upload")
public String uploadFile(@Schema(
description = "文件", // 参数的详细描述
type = "string", // 定义字段的基础数据类型,虽然文件本质是二进制数据,但在 OpenAPI 规范中,文件上传需通过 multipart/form-data 编码传输,此时文件字段在请求中会被视为 字符串形式的二进制流标识。
format = "binary" // 指定字段的数据格式为二进制流
) MultipartFile file) throws Exception
{
return "上传成功";
}
除了基础类型参数和文件类型参数,我们一般还有一个实体类类型的参数,配置方式如下:
@Schema(description = "会员基础信息模型")
public class Member {
/** 会员ID */
@Schema(
description = "会员唯一ID", // 字段描述:说明字段含义
example = "1", // 示例值:文档中显示的示例数据
accessMode = Schema.AccessMode.READ_ONLY, // 访问模式:表示该字段仅用于响应(不可修改)
minimum = "10000", // 最小值:限定ID最小值为10000
maximum = "99999", // 最大值:限定ID最大值为99999
type = "integer" // 数据类型:限定为整数类型
)
private Integer memberId;
/** 会员姓名 */
@Schema(
description = "用户名", // 字段描述
minLength = 4, // 最小长度:用户名至少4个字符
maxLength = 20, // 最大长度:用户名最多20个字符
pattern = "^[a-zA-Z0-9_]+$", // 正则规则:只允许字母/数字/下划线
defaultValue = "guest", // 默认值:未传值时使用默认值"guest"
type = "string"
)
private String memberName;
/** 会员手机号 */
@Schema(description = "会员手机号",type = "string")
private String memberPhone;
/** 会员邮箱 */
@Schema(description = "会员邮箱",type = "string")
private String memberEmail;
/** 会员地址 */
@Schema(description = "会员地址",type = "string")
private String memberAddress;
/** 会员类型 */
@Schema(
description = "会员类型", // 字段描述:说明字段含义
type = "short",// 字段类型限制
allowableValues = {"0", "1", "2"}// 限定范围
)
private Short memberType;
@Schema(
description = "角色列表", // 字段描述
implementation = Role.class, // 关联模型:说明数组元素对应的数据模型
type = "array" // 数据类型:明确声明为数组类型(可省略)
)
private List<Role> roles;
}
如果我们有一种需求是,在页面传输的时候使用一个个的参数传递,但是后台使用实体类进行接收,该怎么写呢?如下:
@PostMapping("/add")
@Operation(
// 接口摘要说明(简洁描述接口功能)
summary = "添加会员",
// 详细接口描述(可包含具体行为说明)
description = "添加会员"
)
public String add(@ParameterObject Member member) {
return "请求成功";
}
当Java代码中使用@RequestParam注解接收参数或者不使用注解接收参数的时候,接口将以键值对格式进行传输参数,这种方式可以使用上面讲过的@ParameterObject注解
实体类类型参数不加@ParameterObject@PostMapping("/add")
@Operation(
// 接口摘要说明(简洁描述接口功能)
summary = "添加会员",
// 详细接口描述(可包含具体行为说明)
description = "添加会员"
)
public String add(@RequestParam Member member) {
return "请求成功";
}
JSON格式包含:
注意:这里配置的只是用于页面展示的接口请求返回示例并不是接口真正的返回
@GetMapping("/detail")
@Operation(
// 接口摘要说明(简洁描述接口功能)
summary = "会员详情",
// 详细接口描述(可包含具体行为说明)
description = "会员详情"
)
@ApiResponse(
// 响应状态码
responseCode = "200",
// 响应描述
description = "成功返回会员详情",
content = @Content(
// 响应媒体类型
mediaType = "application/json",
// 定义相应实体类类型
schema = @Schema(implementation = Member.class),
// 定义一个返回示例
examples = {@ExampleObject(
//成功示例1的名称
name = "成功示例1",
//成功示例1的详情
value = """
{
"memberId": 1,
"memberName": "Jack",
"memberPhone": "13111111111",
"memberEmail": "131@qq.com",
"memberAddress": "演示地址",
"memberType": 0
}
"""
),@ExampleObject(
//成功示例2的名称
name = "成功示例2",
//成功示例2的详情
value = """
{
"memberId": 2,
"memberName": "Tom",
"memberPhone": "13222222222",
"memberEmail": "132@qq.com",
"memberAddress": "演示地址2",
"memberType": 1
}
"""
)}
)
)
@ApiResponse(
responseCode = "404",
description = "信息获取失败",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ErrorResult.class),
examples = @ExampleObject(
name = "失败示例",
value = """
{
"code": 404,
"msg": "会员不存在"
}
"""
)
)
)
public Member detail(@Parameter(description = "memberId", required = true) Integer memberId) {
return new Member();
}
点击Try it out即可对接口进行请求
点击Execute发送接口请求
点击Cancel关闭发送请求模式,点击Clear可以清空接口返回
当前模块展示当前Group分组内,所有相关的实体类(不论是请求参数实体类,还是接口返回实体类)
原生的SpringDoc(Swagger)并没有直接提供导出的功能,只能通过访问/v3/api-docs链接访问后,右键进行保存,如下
如果需要更方便、更完整的文档导出功能,可以引入UI增强工具Knife4j进行导出
引入Knife4j增强
大家在使用SpringDoc的时候,有没有一种,UI界面不够美观,不够好用的感觉呢?如果有,Knife4j就是你的救星,不用怀疑,他仅仅只是在SpringDoc的基础上,增加了一些小功能,同时优化了SpringDoc的UI界面
<!-- 替换SpringDoc的Jar包为Knife4j-->
<!-- 删除这个-->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.5.0</version>
</dependency>
<!-- 新增这个-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.1.0</version>
</dependency>
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!