2228 字
约 7 分钟
19
S06-X02 · Knife4j:给 Spring Boot 接口文档加一层可用界面

接口文档有两种失败方式:一种干脆没有,另一种是生成了 Swagger UI 但没人愿意打开——一个页面堆几百个接口,展开靠手点,搜索靠 Ctrl+F,请求示例要自己拼 JSON。Knife4j 治的是后一种:它不生成 OpenAPI 描述,只把 springdoc 产出的那份 JSON 换一套前端来渲染,再补上调试面板、全局参数、离线导出和分组树。

一句话定位:springdoc 决定"文档是什么",Knife4j 决定"文档怎么看"。认清这条分界,后面每个配置项才有地方安放。


1. 它到底是什么:一层 UI,不是另一套扫描器

层 组件 负责 不该指望它
描述生成 springdoc-openapi 2.x 扫注解与 Controller 签名,输出 /v3/api-docs JSON 好看的界面
渲染前端 Knife4j 的 doc.html + webjars 分组树、在线调试、全局参数、离线文档 生成描述
增强后端 knife4j-openapi3-jakarta-spring-boot-starter 挂 UI 资源、提供鉴权与屏蔽开关 替代 springdoc

所以 /doc.html 打开是白屏时,九成不是 Knife4j 坏了,是它拿不到 JSON。先直接访问 /v3/api-docs,有 JSON 才能谈界面。

2. 依赖选择:Boot 3 只认 jakarta 那一条

// Spring Boot 3.3.x / JDK 17 的 Maven 坐标
// <dependency>
//   <groupId>com.github.xiaoymin</groupId>
//   <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
//   <version>4.5.0</version>
// </dependency>
场景 坐标 说明
Boot 3.x / JDK 17 com.github.xiaoymin:knife4j-openapi3-jakarta-spring-boot-starter:4.5.0 jakarta.* 命名空间,随包带入 springdoc 2.x
只要 UI,不要后端增强 com.github.xiaoymin:knife4j-openapi3-ui 自己另配 springdoc 版本
Boot 2.x com.github.xiaoymin:knife4j-openapi3-spring-boot-starter 非 jakarta,和上一条不能混
Boot 2.x + swagger2 com.github.xiaoymin:knife4j-spring-boot-starter:3.x 老坐标,停在维护状态
只要原生界面 org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0 没有 Knife4j 的增强面板

最容易踩的一条:Boot 3 工程里填了 knife4j-spring-boot-starter。启动不报错,/doc.html 却 404 或空白——那条依赖链走的是 springfox + javax.servlet,jakarta 环境下扫不到任何 Controller,JSON 是空的。坐标对不上版本,后面全白干。

如果 4.5.0 带入的 springdoc 与 Boot 3.3 的 ControllerAdviceBean 之类内部 API 打架(典型报 NoSuchMethodError),显式把 springdoc-openapi-starter-webmvc-ui 钉到 2.6.0 覆盖传递版本,具体以官方 4.5.0 依赖树核对。

3. 最小配置:五行 yaml 起站

springdoc:
  api-docs:
    enabled: true
    path: /v3/api-docs
  packages-to-scan: com.example.clarity.web
  paths-to-match: /api/**

knife4j:
  enable: true
  setting:
    language: zh_cn
键 作用 不配的后果
springdoc.api-docs.path 描述 JSON 路径 默认 /v3/api-docs,改了要同步改网关聚合地址
springdoc.packages-to-scan 限定扫描包 全量扫描,启动慢且把内部接口一起暴露
springdoc.paths-to-match 按 URL 过滤 写的是 Controller 映射,不含 context-path
knife4j.enable 是否挂 UI false 时 /doc.html 直接 404
knife4j.setting.language 界面语言 默认英文,zh_cn 才有中文文案

访问地址是 http://localhost:8080/doc.html。若配了 server.servlet.context-path=/clarity,实际是 /clarity/doc.html——大量"404"就死在这里,Spring Boot 3 的 context-path 会作用到静态资源。

4. 注解:三个注解各管一段

@Tag(name = "文章管理", description = "博客文章的增删改查")
@RestController
@RequestMapping("/api/articles")
public class ArticleController {

    @Operation(summary = "分页查询文章", description = "按发布时间倒序,可按标签过滤")
    @GetMapping
    public PageResult<ArticleVO> page(
            @Parameter(description = "页码,从 1 开始") @RequestParam(defaultValue = "1") int page,
            @Parameter(description = "每页条数") @RequestParam(defaultValue = "20") int size) {
        return service.page(page, size);
    }
}
注解 写在哪 常见误用
@Tag Controller 类 写成 swagger2 的 @Api,Boot 3 下完全无效
@Operation 方法 summary 写一句话,细节塞 description
@Parameter 方法参数 基本类型不写就显示成 param1
@Schema DTO 类/字段 字段不写,界面没有中文说明和示例值
@Hidden 类/方法/字段 内部接口用它,比配 paths-to-exclude 精确
@Schema(description = "文章创建入参")
public record ArticleCreateDTO(
        @Schema(description = "标题", example = "分片路由的四步",
                requiredMode = Schema.RequiredMode.REQUIRED) @NotBlank String title,
        @Schema(description = "正文 Markdown", example = "# 标题\n正文") @NotBlank String content,
        @Schema(description = "标签", example = "[\"java\",\"db\"]") List<String> tags,
        @Schema(hidden = true) String operator) {}

record 在 springdoc 2.x 下能正常解析构造参数上的 @Schema,前提是注解写在参数上而不是 record 组件外。

5. 分组与聚合:两套机制别混用

单体应用内分组用 group-configs,每个分组是一个独立的 JSON 端点:

springdoc:
  group-configs:
    - group: blog-web
      packages-to-scan: com.example.clarity.web
      paths-to-match: /api/**
    - group: blog-admin
      packages-to-scan: com.example.clarity.admin
      paths-to-match: /admin/**

网关聚合是另一回事。knife4j-gateway-spring-boot-starter 属于 Spring Cloud Gateway 生态,从注册中心拉服务清单自动拼;而在单体或多服务手工清单场景,用 Spring 原生的静态地址列表更稳:

springdoc:
  swagger-ui:
    urls:
      - name: blog
        url: /blog/v3/api-docs
      - name: quiz
        url: /quiz/v3/api-docs

区别是:静态清单需要你保证每个 url 可达且 CORS 放行;自动聚合需要网关能发现实例,实例下线后菜单会残留。

6. 生产环境怎么关

做法 效果 代价
knife4j.production=true 文档资源被 Knife4j 自己的过滤器拦下 依赖仍在,类加载开销照付
springdoc.api-docs.enabled=false 根本不生成 JSON /doc.html 打开是空壳
Maven 按 profile 不打包 生产 jar 里没有 UI 资源 构建矩阵变复杂
nginx / 网关 deny 外部访问不到 内网仍可直达

按官方 4.5.0 文档,knife4j.production=true 后文档资源不可访问;但它拦的是 Knife4j 的资源路径,/v3/api-docs 是否仍直接暴露取决于 springdoc 版本。稳妥组合是两个都关:

# application-prod.yaml
springdoc:
  api-docs:
    enabled: false
knife4j:
  enable: false
  production: true
# application-dev.yaml
springdoc:
  api-docs:
    enabled: true
knife4j:
  enable: true
  production: false
  basic:
    enable: true
    username: doc
    password: CHANGE_ME

knife4j.basic 的密码明文躺在配置文件里,只能当内网挡板,不能当认证。

7. 鉴权体系下的文档访问

Spring Security 默认拦一切时,文档页必须显式放行这几类资源:

路径 用途 漏放行的表现
/doc.html UI 入口 302 跳登录页或 401
/webjars/** JS / CSS 白屏、样式全丢
/v3/api-docs/** 描述 JSON 左侧分组树空
/swagger-resources/** 分组元数据 分组下拉只有一项或为空
@Bean
SecurityFilterChain docChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests(auth -> auth
            .requestMatchers("/doc.html", "/webjars/**", "/v3/api-docs/**",
                             "/swagger-resources/**", "/favicon.ico").permitAll()
            .anyRequest().authenticated());
    return http.build();
}

接口本身需要登录时,调试面板里的请求会 401——这不是 Knife4j 的问题,是 UI 不带你的 Cookie/Token。解决方式是全局参数:

knife4j:
  setting:
    enable-swagger-models: true

然后在界面的"全局参数设置"里加一个 Authorization header,它会附加到该分组的所有调试请求上。

8. 排错:六种打不开

现象 原因 处理
/doc.html 404 依赖是 springfox 老坐标 / knife4j.enable=false / context-path 没带 换 jakarta 坐标,检查 server.servlet.context-path
页面打开但白屏 /webjars/** 或 /v3/api-docs 被拦 放行静态资源,F12 看哪个请求 401/404
分组树是空的 扫描包没覆盖,或 @EnableWebMvc 破坏了自动配置 去掉 @EnableWebMvc,放宽 packages-to-scan
JSON 里接口重复两遍 同一个类被两个 group-configs 匹配 用不重叠的 paths-to-match
启动报 NoSuchMethodError springdoc 版本与 Boot 3.3 不匹配 显式钉 springdoc 版本
上传接口调试报 415 文档里没声明 multipart/form-data 用 @RequestBody 配 @Schema 或改用 @RequestPart

@EnableWebMvc 这条特别隐蔽:它会让 Spring Boot 的 WebMvc 自动配置整体退让,springdoc 的资源注册器随之失效,表现是 /v3/api-docs 404 而不是报错。

9. 边界:什么时候别上 Knife4j

场景 更合适的做法 理由
纯前后端交付、需要契约先行 OpenAPI 文件 + codegen 手写注解生成的契约不可评审
对外公开 API 独立文档站(Redoc / Stoplight) UI 和线上鉴权解耦
服务超过十个 网关聚合 + 统一门户 每个服务各挂一份 UI 没法用
只需要偶尔看一次 /v3/api-docs + 任意 JSON 查看器 不值得为界面加一个依赖
生产必须零额外攻击面 构建期生成静态 HTML 运行期不留任何文档端点

10. 自测

  1. /doc.html 能打开但分组树为空,第一步该直接访问哪个地址?
  2. Boot 3 工程里用了 knife4j-spring-boot-starter,为什么启动不报错却扫不到接口?
  3. springdoc.paths-to-match 里要不要带 server.servlet.context-path?
  4. knife4j.production=true 和 springdoc.api-docs.enabled=false 各关掉了什么?
  5. 安全配置里除了 /doc.html,还有哪三类路径必须放行?
  6. 什么时候该用 group-configs,什么时候该用 springdoc.swagger-ui.urls?
S06-X02 · Knife4j:给 Spring Boot 接口文档加一层可用界面
https://www.clxhxhhr.top/posts/5649/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。