Springboot 2.7 集成 Swagger 增强版接口框架 Knife4j 4.3 + springdoc OpenApi 3.0
参考原文链接:https://blog.csdn.net/Mrqiang9001/article/details/132305015
1 摘要
Swagger 作为一款服务端接口文档自动生成框架,早已深入人心,并且在市场上得到了广泛的应用。然而,Swagger 3.0 也就是 OpenApi 3.0 规范发布之后便停止了更新维护,出道就是巅峰。Knife4j 作为 Swagger 的增强版,是对 Swagger UI 做了优化,同时还有很多增强的功能。伴随着 Swagger 3.0 的停止更新,如今 Knife4j 从4.0开始已经逐渐使用 Springdoc 作为 swagger 的替代。Springdoc 针对 OpenApi 3.0 的适配做了较大的调整,其中注解与 Swagger 2 的基本不通用。作为新项目而言,使用社区维护活跃的开源框架显得非常重要。本文将介绍基于 Springboot 2.7 集成 Swagger 的增强框架 Knife4j 4.3 + Springdoc OpenApi 3.0 。
Knife4j 官方文档: https://doc.xiaominfo.com/docs/quick-start
Springdoc 官方文档: https://springdoc.org
SpringDoc注解解析:https://blog.csdn.net/qq_29012499/article/details/135433483
解决接口模块儿排序问题:knife4j、swagger、springdoc 返回接口分组排序问题 https://blog.csdn.net/neusoft2016/article/details/130975683
2 核心 Maven 依赖
本示例中使用的是 Springboot 2.7 版本,对应的 maven 依赖为:
<!-- knife4j openapi3 接口文档 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
<version>4.3.0</version>
</dependency>
3 核心代码
application.yml
# spring-doc 接口文档
springdoc:
api-docs:
enabled: true # 是否启用接口文档
knife4j:
enable: true # 是否启用 knife4j 增强,如果只是使用 knife4j 的 UI,则可以关闭
3.2 openApi 配置类
package com.sunmon.turtle.config;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import org.apache.commons.lang3.StringUtils;
import org.springdoc.core.GroupedOpenApi;
import org.springdoc.core.customizers.OpenApiCustomiser;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Comparator;
import java.util.stream.Collectors;
@OpenAPIDefinition(
security = @SecurityRequirement(name = "Authorization")
)
@SecurityScheme(type = SecuritySchemeType.APIKEY, name = "Authorization", scheme = "Authorization", in = SecuritySchemeIn.HEADER)
@Configuration
public class OpenApiConfig {
private String title = "SpringDoc API";
private String description = "Knife4j OpenApi 3 description";
private String version = "v0.0.1";
@Bean
public OpenAPI springOpenAPI() {
return new OpenAPI()
.info(new Info()
.title(title)
.description(description)
.version(version));
}
@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.group(title)
.pathsToMatch("/**")
.addOpenApiCustomiser(sortTagsAlphabetically())
.build();
}
//排序规则方法,Controller 排序
public OpenApiCustomiser sortTagsAlphabetically() {
return openApi -> openApi.setTags(openApi.getTags()
.stream()
.sorted(Comparator.comparing(tag -> StringUtils.stripAccents(tag.getName())))
.collect(Collectors.toList()));
}
}
3.3.2 DTO
package com.sunmon.turtle.domain.dto;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.ToString;
import javax.validation.constraints.NotNull;
@Data
@ToString(callSuper = true)
@Schema(description = "用户表单实体")
@TableName(value = "user")
public class UserFormDTO {
@NotNull(message = "注册手机号 不能为空")
@Schema(description = "注册手机号", requiredMode = Schema.RequiredMode.REQUIRED)
private String phone;
@Schema(description = "用户id")
private Long id;
@Schema(description = "用户名")
private String userName;
@Schema(description = "密码")
private String password;
@Schema(description = "详细信息,JSON风格")
private String info;
@Schema(description = "账户余额")
private Integer balance;
}
@Schema 注解中参数是否必填使用了一个枚举类 io.swagger.v3.oas.annotations.media.Schema.RequiredMode 这是 @Schema 注解的一个内部类,支持 3 种模式, @Schema 注解中多种属性也采用了枚举类的方式,这是与原来的 @ApiModelPropertity 注解区别比较大的地方。
3.3.3 PO
package com.sunmon.turtle.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.ToString;
@Data
@ToString(callSuper = true)
@Schema(description = "用户 VO 实体")
public class UserVO {
@Schema(description = "用户id")
private Long id;
@Schema(description = "用户名")
private String userName;
@Schema(description = "详细信息")
private String info;
@Schema(description = "用户状态(1启用 0停用)")
private String status;
@Schema(description = "账户余额")
private Integer balance;
}
这里需要注意的是公共返回结果类上不能使用 @Schema 注解,尤其是在指定 name 的情况下,这会使生成的接口文档没有对应泛型类的属性,所有的返回结果都是一样的,看不到返回结果具体对象的属性,增加了前后端对接成本,这不符合我们对于接口文档的要求。
于此同时,对于泛型类 data 参数,也不要使用 @Schema 注解,对于没有使用 lombok 插件,而手动书写 getter/setter 的方式,data 的 getter 方法返回结果也必须是 T 而不能是 Object 类型,否则也无法生成对应泛型类的属性文档。
3.4 VO 示例
package com.sunmon.turtle.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.ToString;
@Data
@ToString(callSuper = true)
@Schema(description = "用户 VO 实体")
public class UserVO {
@Schema(description = "用户id")
private Long id;
@Schema(description = "用户名")
private String userName;
@Schema(description = "详细信息")
private String info;
@Schema(description = "用户状态(1启用 0停用)")
private String status;
@Schema(description = "账户余额")
private Integer balance;
}
3.5 Controller 控制层示例
package com.sunmon.turtle.controller;
import cn.hutool.core.bean.BeanUtil;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSort;
import com.sunmon.turtle.domain.dto.UserFormDTO;
import com.sunmon.turtle.domain.vo.UserVO;
import com.sunmon.turtle.domain.po.User;
import com.sunmon.turtle.service.IUserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Tag(name = "1 - 用户管理接口", description = "用户管理")
@RequestMapping("/users")
@RequiredArgsConstructor//构造时,用必要的参数生成构造函数
@RestController
public class UserController {
private final IUserService userService;
@Operation(summary = "01 用户新增接口")
@PostMapping
public void saveUser(@RequestBody UserFormDTO userDTO) {
//1.把 DTO拷贝到PO
User user = BeanUtil.copyProperties(userDTO, User.class);//HuTool工具包
//2.新增
userService.save(user);
}
@Operation(summary = "02 根据 id 查询用户接口",
parameters = {@Parameter(name = "用户id", required = true, in = ParameterIn.PATH)})
@GetMapping("{id}")
public UserVO queryUserById(@PathVariable("id") Long id) {
User user = userService.getById(id);
return BeanUtil.copyProperties(user, UserVO.class);//HuTool工具包
}
@Operation(summary = "03 删除用户接口",
parameters = {@Parameter(name = "用户id", required = true, in = ParameterIn.PATH)}
)
@DeleteMapping("{id}")
public void deleteUserById(@PathVariable("id") Long id) {
userService.removeById(id);
}
@Operation(summary = "04 根据 id 批量查询用户接口",
parameters = {@Parameter(name = "用户id集合", required = true, in = ParameterIn.PATH)})
@GetMapping
public List<UserVO> queryUserByIds(@RequestParam("ids") List<Long> ids) {
List<User> users = userService.listByIds(ids);
return BeanUtil.copyToList(users, UserVO.class);//HuTool工具包
}
@Operation(summary = "05 扣减用户余额接口",
parameters = {@Parameter(name = "用户id", required = true, in = ParameterIn.PATH),
@Parameter(name = "扣减的金额", required = true, in = ParameterIn.PATH)}
)
@PutMapping("/{id}/deduction/{money}")
public void deductBalance(@PathVariable("id") Long id, @PathVariable("money") Integer money
) {
userService.deductBalance(id, money);
}
}
在 Controller 类中,原来 Swagger 2 的 @Api 注解替换为 @Tag, 原来的 @ApiOperation 注解替换为 @Operation。
在请求头中的参数,例如 GET 请求,如果是一个对象,则需要使用 @ParameterObject 注解,如果不添加,则无法在 Swagger 接口文档界面进行正常请求。
3.6 界面
————————————————
4 升级注意事项(踩坑指南)
4.1 Swagger 2 注解替换
Replace swagger 2 annotations with swagger 3 annotations (it is already included with springdoc-openapi-starter-webmvc-ui dependency). Package for swagger 3 annotations is io.swagger.v3.oas.annotations.
@Api → @Tag
@ApiIgnore → @Parameter(hidden = true) or @Operation(hidden = true) or @Hidden
@ApiImplicitParam → @Parameter
@ApiImplicitParams → @Parameters
@ApiModel → @Schema
@ApiModelProperty(hidden = true) → @Schema(accessMode = READ_ONLY)
@ApiModelProperty → @Schema
@ApiOperation(value = "foo", notes = "bar") → @Operation(summary = "foo", description = "bar")
@ApiParam → @Parameter
@ApiResponse(code = 404, message = "foo") → @ApiResponse(responseCode = "404", description = "foo")
This step is optional: Only if you have multiple Docket beans replace them with GroupedOpenApi beans.
Migrating from SpringFox
————————————————
4.2 公共返回参数不能使用 @Schema
注解,否则就会只生成一个 Swagger Model
4.3 Get 请求参数前需要添加 @ParamterObject
注解
springdoc-openapi 在 1.6.11 版本中添加了一个配置,可以一键解决不使用 @ParamterObject
注解 Swagger 接口测试不可用的问题,配置如下:
springdoc: # 默认是false,需要设置为true default-flat-param-object: true
然而这么做之后,所有通过 body 传的参数也会变成请求头传递,这只是把问题转移了,并没有从根本上解决问题。
针对这一点,对于想要尝鲜的用户还是要注意书写规范,否则改动量就非常大了。
Knife4j v4.0版本针对参数解析ParameterObject的问题说明
5 使用效果
5.1 导出到 Postman 等测试工具的接口数据地址
http://127.0.0.1:9050/v3/api-docs
————————————————
5.2 Knife4j 导出接口文档
5.3 Knife4j UI 界面
地址:
http://127.0.0.1:9000/doc.html
6 推荐参考资料
Knife4j 官方文档: https://doc.xiaominfo.com/docs/quick-start
Springdoc 官方文档: https://springdoc.org
【超详细】springboot + springdoc-openapi + knife4j 集成案例
swagger(三):统一返回结果不显示字段说明
Knife4j v4.0版本针对参数解析ParameterObject的问题说明
————————————————
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?