前言
我们知道利用Swagger-UI结合Swagger提供的注解,在SpringBoot项目上可以将接口以HTML形式(swagger-ui.html)呈现出来,并且可以在线调试。但是老外的审美和使用习惯可能不太符合中国开发者的喜好。于是Knife4J(https://doc.xiaominfo.com)便诞生了,默认提供的界面通过左侧分组+右侧接口展现的形式十分符合中国开发者的习惯,然后版本太多,官网也是花了大量的篇幅阐述不同版本之间的差异,本文就着重介绍下使用使用SpringBoot2+SpringDoc(https://doc.xiaominfo.com/docs/quick-start#openapi3)的方式来实现OpenAPI3规范的Swagger文档。
添加依赖
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
<version>4.5.0</version>
</dependency>
示例接口
org.example.controller.pack1.Demo1.java
package org.example.controller.pack1;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.Data;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.NotBlank;
import java.util.Collections;
import java.util.List;
@Tag(name = "DEMO1")
@RestController
@RequestMapping("demo1")
public class Demo1 {
@Operation(summary = "Find Demo1 List")
@GetMapping("findList")
public List<Demo1Resp> findList(Demo1Req req) {
return Collections.EMPTY_LIST;
}
@Operation(summary = "Delete Demo1 By ID")
@DeleteMapping("deleteById")
public Boolean deleteById(@Parameter(description = "Demo1 ID") Long id) {
return false;
}
@Data
@Schema(description = "Demo1 Request")
public static class Demo1Req {
@NotBlank
@Schema(description = "Demo1 Code")
private String code;
@Schema(description = "Demo1 Name")
private String name;
}
@Data
@Schema(description = "Demo2 Response")
public static class Demo1Resp {
@Schema(description = "Demo1 ID")
private Long id;
@Schema(description = "Demo1 Code")
private String code;
@Schema(description = "Demo1 Name")
private String name;
}
}
org.example.controller.pack2.Demo2.java
package org.example.controller.pack2;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.Data;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.NotNull;
import java.util.Collections;
import java.util.List;
@Tag(name = "DEMO2")
@RestController
@RequestMapping("demo2")
public class Demo2 {
@Operation(summary = "Find Demo2 List")
@GetMapping("/findList")
public List<Demo2Resp> findList(Demo2Req req) {
return Collections.EMPTY_LIST;
}
@Operation(summary = "Delete Demo2 By ID")
@DeleteMapping("deleteById")
public Boolean deleteById(@Parameter(description = "Demo2 ID") Long id) {
return false;
}
@Data
@Schema(description = "Demo2 Request")
public static class Demo2Req {
@NotNull
@Schema(description = "Demo2 Code")
private String code;
@Schema(description = "Demo2 Name")
private String name;
}
@Data
@Schema(description = "Demo2 Response")
public static class Demo2Resp {
@Schema(description = "Demo2 ID")
private Long id;
@Schema(description = "Demo2 Code")
private String code;
@Schema(description = "Demo2 Name")
private String name;
}
}
默认效果
以上的接口代码,在没有做任何配置(包括application.yml和@Configuration配置)的情况
下,打开浏览器访问http://localhost:8080/doc.html将显示以下画面
- 主页
- Find Demo1 List接口
- Find Demo2 List接口
- Swagger Models
同时,如果访问http://localhost:8080/swagger-ui/index.html,那么默认的Swagger-UI页面也是可以呈现的:
Springdoc配置
由于使用该版本的Knife4J是基于Springdoc(https://springdoc.org)的,所以可以使用Springdoc的配置来自定义一部分效果
常用配置
参数名称 | 默认值 | 参数描述 |
---|---|---|
springdoc.api-docs.enabled | true | 开始ApiDocs的接口访问,默认地址:/v3/api-docs,关闭后接口文档将无法访问 |
springdoc.swagger-ui.enabled | true | 是否开启Swagger-UI视图,默认地址:/swagger-ui.html, 关闭后接口文档将无法访问 |
springdoc.packages-to-scan | * | 扫描的包路径,多个用逗号隔开 |
springdoc.packages-to-exclude | 包扫描排除的路径,多个用逗号隔开 | |
springdoc.paths-to-match | /* | 扫描的接口URL路径,多个用逗号隔开,支持AntPath形式 |
springdoc.paths-to-exclude | 排除的接口URL路径,多个用逗号隔开,支持AntPath形式 | |
springdoc.auto-tag-classes | true | 是否开启自动Tag(没有@Tag注解类,会将类名以横杠隔开的形式自动生成一个Tag) |
springdoc.api-docs.groups.enabled | true | 是否允许分组 |
springdoc.group-configs[0].group | 分组名称 | |
springdoc.group-configs[0].display-name | 分组显示名称 | |
springdoc.group-configs[0].packages-to-scan | * | 扫描的包路径,多个用逗号隔开 |
springdoc.group-configs[0].packages-to-exclude | 包扫描排除的路径,多个用逗号隔开 | |
springdoc.group-configs[0].paths-to-match | /* | 扫描的接口URL路径,多个用逗号隔开,支持AntPath形式 |
springdoc.group-configs[0].paths-to-exclude | 排除的接口URL路径,多个用逗号隔开,支持AntPath形式 | |
springdoc.pre-loading-enabled | false | 是否开启预加载,开启后在启动时就会生成OpenApi文档 |
springdoc.writer-with-order-by-keys | false | 接口是否按照字母顺序排序 |
springdoc.disable-i18n | false | 是否禁用国际化 |
springdoc.default-flat-param-object | false | 扁平显示属性值(开启后直接显示实体内的参数,而不显示实体名称) |
springdoc.show-actuator | false | 是否显示Actuator接口路径 |
springdoc.show-spring-cloud-functions | true | 是否显示SpringCloud接口路径 |
其他配置
其他配置参考官网(https://springdoc.org/#springdoc-openapi-core-properties)
Knife4j增强配置
针对Knife4j的Swagger文档,提供一些个性化的配置:
参数名称 | 默认值 | 参数描述 |
---|---|---|
knife4j.enable | false | 是否开启Knife4j增强模式 |
knife4j.cors | false | 是否开启一个默认的跨域配置,该功能配合自定义Host使用 |
knife4j.production | false | 是否开启生产环境保护 |
knife4j.basic.enable | false | 启用文档BasicHttp校验功能,保护文档 |
knife4j.basic.username | basic用户名 | |
knife4j.basic.password | basic密码 | |
knife4j.documents | 自定义文档集合,该属性是数组 | |
knife4j.documents.group | 所属分组 | |
knife4j.documents.name | 类似于接口中的tag,对于自定义文档的分组 | |
knife4j.documents.locations | markdown文件路径,可以是一个文件夹(classpath:markdowns/*),也可以是单个文件(classpath:md/sign.md) | |
knife4j.setting.enable-after-script | true | 调试Tab是否显示AfterScript功能,默认开启 |
knife4j.setting.language | zh-CN | Ui默认显示语言,目前主要有两种:中文(zh-CN)、英文(en-US) |
knife4j.setting.enable-swagger-models | true | 是否显示界面中SwaggerModel功能 |
knife4j.setting.swagger-model-name | Swagger Models | 重命名SwaggerModel名称,默认 |
knife4j.setting.enable-document-manage | true | 是否显示界面中"文档管理"功能 |
knife4j.setting.enable-reload-cache-parameter | false | 是否在每个Debug调试栏后显示刷新变量按钮,默认不显示 |
knife4j.setting.enable-version | false | 是否开启界面中对某接口的版本控制,如果开启,后端变化后Ui界面会存在小蓝点 |
knife4j.setting.enable-request-cache | true | 是否开启请求参数缓存 |
knife4j.setting.enable-filter-multipart-apis | false | 针对RequestMapping的接口请求类型,在不指定参数类型的情况下,如果不过滤,默认会显示7个类型的接口地址参数,如果开启此配置,默认展示一个Post类型的接口地址 |
knife4j.setting.enable-filter-multipart-api-method-type | POST | 具体接口的过滤类型 |
knife4j.setting.enable-host | false | 是否启用自定义Host |
knife4j.setting.enable-host-text | false | 自定义HOST地址 |
knife4j.setting.enable-home-custom | false | 是否开启自定义主页内容 |
knife4j.setting.home-custom-path | 主页内容Markdown文件路径 | |
knife4j.setting.enable-search | true | 是否启用UI界面中的搜索框 |
knife4j.setting.enable-footer | true | 是否显示Footer |
knife4j.setting.enable-footer-custom | false | 是否开启自定义Footer |
knife4j.setting.footer-custom-content | false | 自定义Footer内容 |
knife4j.setting.enable-dynamic-parameter | false | 是否开启动态参数调试功能 |
knife4j.setting.enable-debug | true | 启用调试 |
knife4j.setting.enable-open-api | true | 显示OpenAPI规范 |
knife4j.setting.enable-group | true | 显示服务分组 |
推荐配置
application.properties
# 开启预加载
springdoc.pre-loading-enabled = true
# 接口按照字母顺序排序
springdoc.writer-with-order-by-keys = true
# 接口分组1
springdoc.group-configs[0].group = 分组1
# 分组1包扫描路径
springdoc.group-configs[0].packages-to-scan = org.example.controller.pack1
# 接口分组2
springdoc.group-configs[1].group = 分组2
# 分组2URL匹配路径(支持Ant)
springdoc.group-configs[1].paths-to-match = /demo2/*
# 开启Knife4j增强配置
knife4j.enable = true
#隐藏SwaggerModel功能
knife4j.setting.enable-swagger-models = false
#隐藏OpenApi描述
knife4j.setting.enable-open-api = false
# 显示刷新变量按钮
knife4j.setting.enable-reload-cache-parameter = true
# 显示接口变化标识
knife4j.setting.enable-version = true
# 生产环境建议开启文档保护
knife4j.basic.enable = true
# 接口文档访问用户名
knife4j.basic.username = admin
# 接口文档访问密码
knife4j.basic.password = 123456
application.yaml
springdoc:
pre-loading-enabled: true #开启预加载
writer-with-order-by-keys: true #接口按照字母顺序排序
group-configs:
- group: 分组1
packages-to-scan:
- org.example.controller.pack1 # 分组1包扫描路径
- group: 分组2
paths-to-match:
- /demo2/* # 分组2URL匹配路径(支持Ant)
knife4j:
enable: true # 开启Knife4j增强配置
setting:
enable-swagger-models: false #隐藏SwaggerModel功能
enable-open-api: false #隐藏OpenApi描述
enable-reload-cache-parameter: true # 显示刷新变量按钮
enable-version: true # 显示接口变化标识
basic:
enable: true # 生产环境建议开启文档保护
username: admin # 接口文档访问用户名
password: 123456 # 接口文档访问密码
效果如下
- 接口按照包路径或者URL路径分了组
- 隐藏了Swagger Models
- 接口按照字母顺序排序
- OpenApi不显示了
- 接口调试了多了“刷新变量”按钮
已知问题
在用实体对象接收GET请求的参数时,参数默认会以JSON形式去传递:
Knife4J对此提供了两种解决方法, 此类接口数量少的情况下推荐第一种(接口参数中增加@ParameterObject注解):
import org.springdoc.api.annotations.ParameterObject;
@Operation(summary = "Find Demo1 List")
@GetMapping("findList")
public List<Demo1Resp> findList(@ParameterObject Demo1Req req) {
return Collections.EMPTY_LIST;
}
关于第二种方法,使用将参数拉平的配置:
springdoc.default-flat-param-object = true
GET请求的接口是没问题了,但是POST请求时,默认application/json形式的接口(标记了@RequstBody)也被拉平了。
这个是Springdoc的bug(https://github.com/springdoc/springdoc-openapi/pull/2291), 并且官网也对此进行了修复(https://github.com/springdoc/springdoc-openapi/releases/tag/v1.8.0)。目前Knife4J最新版(4.5.0)使用的版本仍是1.7.0,所以只能是先手动强制更新:
pom.xml
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
<version>4.5.0</version>
<exclusions>
<exclusion>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.8.0</version>
</dependency>
但是呢发现更新后,在解析的时候报以下错误:
java.lang.NoSuchMethodError: org.springdoc.core.SpringDocConfigProperties.getGroupConfigs()Ljava/util/List;
at com.github.xiaoymin.knife4j.spring.extension.Knife4jOpenApiCustomizer.addOrderExtension(Knife4jOpenApiCustomizer.java:80) ~[knife4j-openapi3-spring-boot-starter-4.5.0.jar:na]
at com.github.xiaoymin.knife4j.spring.extension.Knife4jOpenApiCustomizer.customise(Knife4jOpenApiCustomizer.java:70) ~[knife4j-openapi3-spring-boot-starter-4.5.0.jar:na]
at org.springdoc.api.AbstractOpenApiResource.lambda$null$7(AbstractOpenApiResource.java:380) ~[springdoc-openapi-common-1.8.0.jar:1.8.0]
…
大致看了下源码,springdoc-openapi-common 1.8.0中org.springdoc.core.SpringDocConfigProperties
类中的groupConfigs
属性类型从原来的List类型换成了Set,所以外面用到的时候出错了。
这时候只能是禁用Knife4J的增强功能了:
knife4j.enable = false
这样两种请求就都可以使用了:
GET(application/x-www-form-urlencoded)
POST(application/json)