Skip to content

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+jsonapplication/openapi+yaml类型返回的HTPP网络资源。

在OAD中,如果存在多个文档描述API,那么肯定存在一个入口文档,这个文档就是Entry document,入口文档名称为openapi.jsonopenapi.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对象中,除了openapiinfo字段是必须的,还需要以下字段之一:

  • 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 defined

1.3 描述接口

1.3.1 概述

本小节介绍如何定义paths对象,即如何定义API端点的出入参。

paths是一个对象,或者说是一个map,paths中的每一个字段(key)表示端点路径,端点路径必须以反斜线/开头。例如:

yaml
paths:
	/book: ...
	/student: ...

TIP

为什么paths是对象而不是数组,是因为端点路径必须唯一,使用map结构,在JSON或YAML语法层面上就可以校验了,而不用进行OpenAPI规范校验。

每一个端点路径的值又是一个对象,称为Paths Item Object,该对象的字段名称是HTTP请求方法,例如getpost等,例如:

yaml
paths:
	/book: 
		get: ...

HTTP请求方法字段的值是一个称为Operation Object的对象,在这个对象中,描述了该端点的概述、请求参数、返回值等信息,例如:

yaml
paths:
	/book: 
		get: 
			summary: 概述
			description: 描述
			parameters: 请求参数
			requestBody: 请求体
			responses: 响应

1.3.2 请求参数

在描述端点时,请求参数分为两类:parametersrequestBody

  • parameters用来定位资源,可以在Path Item ObjectOperation Object中定义,是Parameter Object数组,简单来说,parameters参数提供了path\query parameter\header\cookie参数;
  • requestBody用来提供额外信息,可以在Path Item Object中定义,是Request Body Object(与Response Object相同),简单来说,requestBody参数提供了请求体参数;

以上结构可以用如下图表示:

image-20260202203528269

TIP

注意,parametersPath Item ObjectOperation 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规范的返回结果。

以上结构可以使用如下示意图表示:

image-20260125162912471

image-20260202202437542

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软件,查看接口定义:

image-20260202210344069

以上只是简单介绍了一下OpenAPI规范,对此不做深入研究,如有需要,参考资料[2]。

2. springdoc-openapi

2.1 概述

springdoc-openapi是一个自动生成API文档的Java依赖,它在应用运行时通过反射和 Spring 容器扫描来完成以下工作:

  • 识别路由:扫描 @RestController@RequestMapping
  • 推断数据结构:观察方法的返回值类型和参数类型;
  • 整合配置:读取 application.propertiesapplication.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: 服务器地址,如果是在本地,一般为localhost127.0.0.1

port: 应用端口,例如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

在浏览器中分别访问以下地址:

image-20260202213351638

image-20260202213512422

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,可选值有DEFAULTPATHQUERYHEADERCOOKIE
    • 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;
}

结果如下:

image-20260203200726929

2.4 配置介绍

springdoc-openapi提供了一些配置项,我们可以在application.yml中修改这些配置项,以达成某些目的。

OpenAPI 核心配置如下:

配置项 (以 springdoc.api-docs 开头)默认值说明
.path/v3/api-docs生成 OpenAPI 描述文件的路径(JSON 格式)。
.enabledtrue是否开启生成 OpenAPI 元数据的功能。
.packages-to-scan*指定扫描的包路径(逗号分隔),缩小文档范围。
.paths-to-match/*匹配的接口路径规则(如 /api/v1/**)。

Swagger UI核心配置如下:

配置项 (以 springdoc.swagger-ui 开头)默认值说明
.path/swagger-ui.html访问 Swagger UI 接口文档的 HTML 路径。
.enabledtrue是否启用 Swagger UI 界面。生产环境建议设为 false
.filterfalse是否开启搜索过滤框(当接口很多时非常有用)。
.tryItOutEnabledfasle是否默认激活 "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-durationfalse是否在调试时显示接口请求耗时。

其他配置:

配置项默认值说明
springdoc.default-consumes-media-typeapplication/json默认的请求内容类型。
springdoc.default-produces-media-typeapplication/json默认的响应内容类型。

WARNING

建议在生产环境禁用接口文档:

yaml
springdoc:
  swagger-ui:
    # 生产环境禁用整个 UI
    enabled: false
  api-docs:
    # 同时也禁用 JSON 元数据导出
    enabled: false

2.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")));
    }
}

结果如下:

image-20260203205541411

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时必填,可选值bearerbasic等;
  • name:用于设置header名称;对于 type: http + scheme: bearer,这个 name() 会被 UI 忽略,实际永远使用 "Authorization" 请求头;如果是自定义请求头,那么该项填写具体的请求头名称;
  • in():指定凭证放在请求的哪个位置,可选值有SecurityScheme.In.HEADERSecurityScheme.In.QUERYSecurityScheme.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界面,显示如下:

image-20260206210216588

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

image-20260206210355748

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页面:

image-20260203211353811

可以发现,页面简洁了很多。

3.2 增强功能

Knife4j提供了很多增强功能。

3.2.1 权限控制

如果使用Knife4j,那么springdoc以及Knife4j提供的资源接口包括如下:

资源说明
/doc.htmlKnife4j提供的文档访问地址
/v3/api-docsspringdoc提供的实例接口
/v3/api-docs/swagger-configspringdoc提供的分组接口
/v3/api-docs/**分组
/swagger-ui/index.htmlspringdoc提供的文档访问地址

在生产环境中,为了安全起见,不会暴露接口文档地址,在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: 123

3.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

设置完成后,界面如下:

image-20260206212407347

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

image-20260206212543834

参考资料

[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/