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

swagger(三):统一返回结果不显示字段说明

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的问题说明
————————————————

 

posted @   山顶听风  阅读(1065)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示