Appearance
OpenAPI3与Knife4j
本文介绍如何为Spring Boot项目生成接口文档。
1. OpenAPI规范介绍
1.1 概述
OpenAPI 规范(OpenAPI Specification,简称OAS)是用来描述HTTP接口的规范,OAS通常用YAML或JSON语言编写,便于分享和消费。目前最新的OpenAPI版本是3.2.0。
OpenAPI描述(OpenAPI Description,简称OAD)是通过引用(references)相互连接的一个或多个文档,这些文档通常是以.json或.yaml结尾的本地文件,或者是以application/openapi+json 或 application/openapi+yaml类型返回的HTPP网络资源。
在OAD中,如果存在多个文档描述API,那么肯定存在一个入口文档,这个文档就是Entry document,入口文档名称为openapi.json或openapi.yaml,其他文档称为被引用的文档(referenced documents)。
1.2 OAD结构
每一个OAD文档代表一个JSON对象,规范规定,在JSON对象中必须包含以下字段:
openapi:类型为字符串,代表该文档使用的OpenAPI规范版本,例如3.1.0;info:类型为对象,描述了API文档的基本信息,例如描述、作者以及联系信息等,但是在该对象中,只有两个字段是必须的:title:类型为字符串,API文档的名称,例如“GitHub REST API”;version:类型为字符串,API文档的版本,注意,这是自己定义的自己接口文档的版本,例如1.0.0;
在JSON对象中,除了openapi和info字段是必须的,还需要以下字段之一:
paths:用来描述所有的API端点(endpoints),包括它们的参数以及服务端响应;components:一个容器(object),专门用来存放整个 API 文档中可以被多次引用的、可复用的定义;webhooks:OpenAPI 3.1 新增的顶级字段(3.0 用 callbacks 模拟,3.1 正式独立出来),专门描述服务器主动向客户端发送 HTTP 请求的场景,即 Webhook / 回调通知 / 事件推送;
下面是一个满足OAS的最小结构的OAD:
yaml
openapi: 3.1.0
info:
title: A minimal OpenAPI Description
version: 0.0.1
paths: {} # No endpoints defined1.3 描述接口
1.3.1 概述
本小节介绍如何定义paths对象,即如何定义API端点的出入参。
paths是一个对象,或者说是一个map,paths中的每一个字段(key)表示端点路径,端点路径必须以反斜线/开头。例如:
yaml
paths:
/book: ...
/student: ...TIP
为什么paths是对象而不是数组,是因为端点路径必须唯一,使用map结构,在JSON或YAML语法层面上就可以校验了,而不用进行OpenAPI规范校验。
每一个端点路径的值又是一个对象,称为Paths Item Object,该对象的字段名称是HTTP请求方法,例如get、post等,例如:
yaml
paths:
/book:
get: ...HTTP请求方法字段的值是一个称为Operation Object的对象,在这个对象中,描述了该端点的概述、请求参数、返回值等信息,例如:
yaml
paths:
/book:
get:
summary: 概述
description: 描述
parameters: 请求参数
requestBody: 请求体
responses: 响应1.3.2 请求参数
在描述端点时,请求参数分为两类:parameters和requestBody
parameters用来定位资源,可以在Path Item Object和Operation Object中定义,是Parameter Object数组,简单来说,parameters参数提供了path\query parameter\header\cookie参数;requestBody用来提供额外信息,可以在Path Item Object中定义,是Request Body Object(与Response Object相同),简单来说,requestBody参数提供了请求体参数;
以上结构可以用如下图表示:

TIP
注意,parameters在Path Item Object和Operation Object中都可以定义。当在Path Item Object中定义parameters时,该参数可以在多个操作中共享,可以在Operation Object中覆盖,但不可移除。
此处介绍Parameter Object对象中的字段:
name:参数名称,必须提供,并且需要唯一;in:参数的位置,必须提供,可选值如下:path:参数出现在URL路径上,如果in的值为path,则name必须出现在URL路径上,并且使用{}包括;query:参数出现在URL请求参数中;header:参数出现在请求头中;cookie:参数出现在cookie中;
required:表示该参数是否必须提供,非必须,默认值为false;description:用于描述参数;schema:用于定义参数类型,参考JSON Schema;
1.3.3 响应结果
在描述端点时,返回值/响应结果用responses表示,这是一个对象,其中,字段名称为HTTP返回码(例如200,404等),字段值为Response Object。
Response Object有以下两个必要的字段:
description:描述该返回值意义;content:描述该返回值内容;
content字段是一个Map结构,其中key是多媒体类型,值为多媒体对象(Media Type Object),在多媒体对象中,其中的schema字段值定义了遵循JSON Schema规范的返回结果。
以上结构可以使用如下示意图表示:


1.4 综合案例
基于以上的学习,我们可以定义一个关于书籍的查询、修改、删除接口:
Details
yaml
openapi: 3.1.0
info:
title: Book Sample API
version: 1.0.0
paths:
parameters:
- name: id
in: path
required: true
schema:
type: string
description: The unique identifier for a book
/book/{id}:
get:
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: object
properties:
id:
type: string
title:
type: string
author:
type: string
put:
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
title:
type: string
author:
type: string
responses:
'200':
description: Book updated successfully
content:
application/json:
schema:
type: object
properties:
id:
type: string
title:
type: string
author:
type: string
delete:
responses:
'204':
description: Book deleted successfully我们可以将上述文件导入postman软件,查看接口定义:

以上只是简单介绍了一下OpenAPI规范,对此不做深入研究,如有需要,参考资料[2]。
2. springdoc-openapi
2.1 概述
springdoc-openapi是一个自动生成API文档的Java依赖,它在应用运行时通过反射和 Spring 容器扫描来完成以下工作:
- 识别路由:扫描
@RestController和@RequestMapping; - 推断数据结构:观察方法的返回值类型和参数类型;
- 整合配置:读取
application.properties或application.yml中的配置; - 注入注解:最后将代码中手写的 Swagger 注解(如
@Operation)覆盖或补充到自动推断的模型中;
最终,生成符合OpenAPI 3规范的接口文档,有以下产物:
- Swagger UI (HTML):供人类阅读和调试的界面,地址在
http://server:port/context-path/swagger-ui.html; - JSON格式的OpenAPI文档:地址在
http://server:port/context-path/v3/api-docs; - YAML格式的OpenAPI文档:地址在
http://server:port/context-path/v3/api-docs.yaml;
server: 服务器地址,如果是在本地,一般为
localhost或127.0.0.1port: 应用端口,例如8080;
context-path: 程序的上下文路径,例如
admin
2.2 基本案例
首先,引入以下依赖:
xml
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.8.15</version>
</dependency>然后,在spring boot项目中添加以下Controller:
java
@RestController
public class TestController {
@GetMapping("/hello")
public String hello() {
return "hello world";
}
}application.yml配置文件内容如下:
yaml
server:
port: 8080
servlet:
context-path: /demo在浏览器中分别访问以下地址:


TIP
当访问http://localhost:8080/demo/v3/api-docs.yaml时,浏览器会下载YAML格式的OpenAPI文档。
由此可见,在spring boot项目中引入了springdoc-openapi,就很方便地自动生成了接口文档。
2.3 注解介绍
springdoc-openapi可以自动生成识别接口,但是,有些信息需要我们通过注解提供(例如这个接口在业务上的中文名称是什么、某个 String 类型的字段到底是身份证号还是家庭住址、哪些参数是必填的,哪些是有默认值的等),以下是一些常用注解:
@Operation:它被用来修饰 Controller 类里的方法,负责定义单个 API 接口的元数据,参数如下:summary:摘要(建议填写)。简短描述接口功能,会显示在 Swagger UI 的折叠栏上;description:详细描述。支持长文本,用于解释复杂的业务逻辑或注意事项;hidden:是否隐藏该接口,默认值为false;如果设为true,该接口将不会出现在文档中;tags:字符串数组,用来定义接口标签。可以将该接口归类到特定的分组(会覆盖类级别的@Tag);
@Tag:核心作用是对接口进行分类和命名,可在类或方法级别上定义,参数如下:name:标签名称(必填)。这是分组的唯一标识,通常显示为导航栏的标题;description:描述。对这一组接口的功能进行详细说明;
@Hidden:用于隐藏某接口,也可以使用@Operation(hidden = true);@Parameter:用于描述API接口的参数,与OpenAPI 3规范中的parameters参数语意相同,属性如下:name:参数名称;description:参数描述,解释这个参数代表什么(如:“用户唯一 ID”);in:参数位置,类型为ParameterIn,可选值有DEFAULT、PATH、QUERY、HEADER、COOKIE;required:参数是否必填,默认为false(但PATH路径参数默认为true);example:示例值。在 Swagger UI 中默认填充的值,方便直接点击测试;schema:用于定义参数的限制(如:最小值、最大值、枚举值等);hidden:是否隐藏该参数;
@Parameters:主要作用是当需要在一个方法上定义多个@Parameter时,将它们组合在一起。虽然可以直接在方法上写多个@Parameter,但使用@Parameters可以让代码在某些场景下更加整洁;@ApiResponse:用于定义 API 接口的预期响应结果,属性如下:responseCode:HTTP状态码,例如200,404等;description:描述;content:响应内容定义。用于指定返回的数据类型(MIME Type)和引用的数据模型(Schema);headers:响应头。描述返回结果中包含的自定义 Header;
一般情况下,我们会在程序中自己定义一套状态码,HTTP响应一般都是返回200,所以
@ApiResponse用得很少。@Schema:用于定义数据,常见属性如下:title:模型的别名,通常用于类级别;description:字段描述;example:示例值;allowableValues:枚举值范围;minLength / maxLength:字符串长度限制;minimum / maximum:数值大小限制;pattern:正则表达式限制;hidden:是否隐藏字段或实体;
使用以上注解,代码如下:
java
@RestController
@Tag(name = "用户控制器")
public class UserController {
@GetMapping("/user/{id}")
@Operation(summary = "通过ID获取用户信息")
@Parameter(name = "id", description = "用户ID", required = true)
public UserDTO getUser(@PathVariable("id") Long id) {
return null;
}
}java
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "用户信息模型")
@Data
public class UserDTO {
@Schema(description = "用户唯一ID", example = "1024", minimum = "1")
private Long id;
@Schema(description = "用户名", example = "brandy_tech")
private String username;
@Schema(description = "账户状态", allowableValues = {"ACTIVE", "DISABLED"}, defaultValue = "ACTIVE")
private String status;
}结果如下:

2.4 配置介绍
springdoc-openapi提供了一些配置项,我们可以在application.yml中修改这些配置项,以达成某些目的。
OpenAPI 核心配置如下:
| 配置项 (以 springdoc.api-docs 开头) | 默认值 | 说明 |
|---|---|---|
.path | /v3/api-docs | 生成 OpenAPI 描述文件的路径(JSON 格式)。 |
.enabled | true | 是否开启生成 OpenAPI 元数据的功能。 |
.packages-to-scan | * | 指定扫描的包路径(逗号分隔),缩小文档范围。 |
.paths-to-match | /* | 匹配的接口路径规则(如 /api/v1/**)。 |
Swagger UI核心配置如下:
| 配置项 (以 springdoc.swagger-ui 开头) | 默认值 | 说明 |
|---|---|---|
.path | /swagger-ui.html | 访问 Swagger UI 接口文档的 HTML 路径。 |
.enabled | true | 是否启用 Swagger UI 界面。生产环境建议设为 false。 |
.filter | false | 是否开启搜索过滤框(当接口很多时非常有用)。 |
.tryItOutEnabled | fasle | 是否默认激活 "Try it out" 按钮。这个不是说不显示"Try it out"按钮,而是说默认不激活调试模式。 |
.supported-submit-methods | "Try it out"支持的HTTP方法,如果想隐藏"Try it out"按钮,可以把该项配置设置为[],默认支持所有HTTP方法[get, put, post, delete, options, head, patch, trace]。 | |
.display-request-duration | false | 是否在调试时显示接口请求耗时。 |
其他配置:
| 配置项 | 默认值 | 说明 |
|---|---|---|
springdoc.default-consumes-media-type | application/json | 默认的请求内容类型。 |
springdoc.default-produces-media-type | application/json | 默认的响应内容类型。 |
WARNING
建议在生产环境禁用接口文档:
yaml
springdoc:
swagger-ui:
# 生产环境禁用整个 UI
enabled: false
api-docs:
# 同时也禁用 JSON 元数据导出
enabled: false2.5 自定义接口文档
在 springdoc-openapi 中,如果想修改 Swagger UI 顶部显示的标题、版本、描述以及作者联系方式等信息,通常不再通过配置文件,而是通过 Java 配置类 定义一个 OpenAPI Bean。
如下:
java
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("智库系统 API 接口文档") // 自定义文档标题
.version("v1.2.0") // 自定义版本号
.description("这是基于 Spring Boot 3 开发的后端服务接口文档,包含用户管理与数据分析模块。") // 详细描述
.termsOfService("https://example.com/terms") // 服务条款地址
.contact(new Contact() // 作者/联系人信息
.name("xxx团队")
.email("support@example.com")
.url("https://example.com"))
.license(new License() // 许可证信息
.name("Apache 2.0")
.url("https://springdoc.org")));
}
}结果如下:

2.6 安全方案
在OpenAPI 中,安全方案(Security Schemes)是指如何保护接口,也就是定义接口权限。
在 UI 界面上通常表现为右上角的一个锁头图标按钮。设置安全方案主要分为两个步骤:定义方案 和 全局/局部应用。
需要创建一个 OpenAPI 类型的 Bean,在其中定义认证方式,如下:
java
@Bean
public OpenAPI customOpenAPI() {
final String securitySchemeName = "bearerAuth";
final String apiKey = "apiKey";
return new OpenAPI()
// 1. 定义安全方案
.components(new Components()
// 定义方案一
.addSecuritySchemes(securitySchemeName,
new SecurityScheme()
.name(securitySchemeName)
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT"))
// 定义方案二
.addSecuritySchemes(apiKey, new SecurityScheme()
.type(SecurityScheme.Type.APIKEY)
.name("X-Api-Key") // 自定义 header 名字
.in(SecurityScheme.In.HEADER)
.description("自定义API密钥"))
)
// 2. 全局应用两个方案
.addSecurityItem(new SecurityRequirement().addList(securitySchemeName))
.addSecurityItem(new SecurityRequirement().addList(apiKey));
}TIP
关于SecurityScheme各个属性值的介绍如下。
type():必须设置,用于定义认证类型,可选值如下:SecurityScheme.Type.HTTP:定义认证类型为 HTTP 认证。 OpenAPI 中 http 类型专门用于使用 Authorization 头的各种方案(Basic、Bearer、Digest 等)。SecurityScheme.Type.APIKEY:用于自定义请求头;
schema():用于定义具体的 HTTP 认证方案名称,当type=HTTP时必填,可选值bearer、basic等;name:用于设置header名称;对于type: http+scheme: bearer,这个name()会被 UI 忽略,实际永远使用 "Authorization" 请求头;如果是自定义请求头,那么该项填写具体的请求头名称;in():指定凭证放在请求的哪个位置,可选值有SecurityScheme.In.HEADER、SecurityScheme.In.QUERY、SecurityScheme.In.COOKIE,对于type: http+scheme: bearer情况下,规范强制使用HEADER,如果type: APIKEY,该项必填;description():给这个安全方案的详细说明,会显示在文档和 Authorize 弹窗中;bearerFormat():给客户端一个提示:这个 Bearer Token 的格式是什么,常见值:"JWT"、"opaque" 等,仅用于文档说明;
如果不希望全局应用安全方案,只想在特定的 Controller 或方法上开启认证,可以移除 addSecurityItem 全局配置,转而在接口上使用注解:
java
@Operation(summary = "获取用户信息", security = { @SecurityRequirement(name = "bearerAuth") })
@GetMapping("/user/info")
public String getUserInfo() {
return "info";
}当以上设置完成后,在swagger-ui界面,显示如下:

当我们填入值后,点击"Authorize"保存,之后,调试接口时,会发现在请求头中自动带入了我们填入的值:

3. knife4j
3.1 概述
Knife4j 是为 Java 开发者定制的 Swagger UI 增强版。
如果说 Swagger 原生 UI 是“能用但简陋”的毛坯房,那么 Knife4j 就是一套“精装修”的样板间。它在完全遵循 OpenAPI 规范的基础上,极大地优化了界面交互体验,并增加了很多实用功能。
原生 Swagger UI 的界面通常是一条长长的列表,查找接口很不方便。Knife4j 采用了左侧导航栏布局,支持多级目录映射,看起来更像是一个专业的文档系统。
要使用knife4j,只需要把springdoc-openapi依赖改为下面的依赖:
xml
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.5.0</version>
<scope>compile</scope>
</dependency>之后,访问http://ip:port/context/doc.html即可打开knife4j页面:

可以发现,页面简洁了很多。
3.2 增强功能
Knife4j提供了很多增强功能。
3.2.1 权限控制
如果使用Knife4j,那么springdoc以及Knife4j提供的资源接口包括如下:
| 资源 | 说明 |
|---|---|
| /doc.html | Knife4j提供的文档访问地址 |
| /v3/api-docs | springdoc提供的实例接口 |
| /v3/api-docs/swagger-config | springdoc提供的分组接口 |
| /v3/api-docs/** | 分组 |
| /swagger-ui/index.html | springdoc提供的文档访问地址 |
在生产环境中,为了安全起见,不会暴露接口文档地址,在application.yml中可以通过Knife4j提供的配置,轻松关闭:
yaml
knife4j:
# 开启增强配置
enable: true
# 开启生产环境屏蔽
production: true不管是官方的swagger-ui.html或者doc.html,目前接口访问都是无需权限即可访问接口文档的,为了保护接口文档安全,Knife4j提供了密码保护机制,开发者输入用户名和密码来控制界面的访问,只有知道用户名和密码的人才能访问此文档。配置如下:
yaml
knife4j:
# 开启增强配置
enable: true
# 开启Swagger的Basic认证功能,默认是false
basic:
enable: true
# Basic认证用户名
username: test
# Basic认证密码
password: 1233.2.2 afterScript
afterScript是在接口调用之后,执行的一段JavaScript脚本,主要应用场景:针对JWT类型的接口,调用登录接口后,每个接口请求时带上Token参数,此时可以通过该脚本动态赋值全局token参数。
Knife4j目前主要提供ke(Knife4j Environment)对象来获取或者操作全局对象,主要包含的对象:
global:全局操作,可以获取或者设置目前的全局参数setHeader(name,value):设置当前逻辑分组下的全局参数Header请求头setAllHeader(name,value):设置所有逻辑分组下的全局参数Header请求头setParameter(name,value):设置当前逻辑分组下,主要是针对query类型参数进行设置全局设置。setAllParameter(name,value):设置所有逻辑分组下的全局参数,主要是query类型
response:当前请求接口响应内容headers:服务端响应Header对象,注意,此处所有的header的名称全部进行小写转换data:服务端响应数据(json/xml/text等等)
例如,假设登录接口返回如下:
json
{
"code": 8200,
"message": null,
"data": {
"token": "1y1tn8tvw93fxixp79dcx0nugixkw4su"
}
}那么可以编写脚本如下:
javascript
var code=ke.response.data.code;
if(code==8200){
//判断,如果服务端响应code是8200才执行操作
//获取token
var token=ke.response.data.data.token;
//1、如何参数是Header,则设置当前逻辑分组下的全局Header
ke.global.setHeader("token",token);
}3.3 安全方案
如果使用knife4j,那么只在OpenAPI Bean中配置安全方案会失败,还需要在配置类中配置如下:
java
@Bean
public GlobalOpenApiCustomizer globalOpenApiCustomizer() {
return openApi -> openApi.getPaths().values().stream().flatMap(pathItem -> pathItem.readOperations().stream()).forEach(operation -> operation.security(openApi.getSecurity()));
}参考链接:https://github.com/xiaoymin/knife4j/issues/545
设置完成后,界面如下:

当调试具体的接口时,会发现请求头部自动加上了安全方案:

参考资料
[1] 什么是OpenAPI规范:https://www.openapis.org/what-is-openapi
[2] 学习OpenAPI:https://learn.openapis.org/
[3] springdoc官网:https://springdoc.org/
[4] Knife4j:https://doc.xiaominfo.com/