SpringBoot3.3.0集成Knife4j4.5.0实战

原SpringBoot2.7.18升级至3.3.0之后,Knife4j进行同步升级(Spring Boot 3 只支持OpenAPI3规范),从原3.0.3(knife4j-spring-boot-starter)版本升级至4.5.0(knife4j-openapi3-jakarta-spring-boot-starter),以下是升级过程与注意事项等

版本信息

  • JDK 21
  • Maven 3.9.6
  • SpringBoot 3.3.0
  • Knife4j 4.5.0(截止2024-06-18最新仍为4.5.0)

一、pom.xml引入依赖

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.3.0</version><!-- 2.7.18↑--><relativePath/>
</parent><dependencies>...<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId><version>4.5.0</version></dependency>...
</dependencies>

二、yml中配置

# Knife4j配置
# springdoc-openapi配置
springdoc:# get请求多参数时不需要添加额外的@ParameterObject和@Parameter注解default-flat-param-object: true# 启用swaggerUIswagger-ui:#自定义swagger前端请求路径,输入http:127.0.0.1:8080/swagger-ui.html会自动重定向到swagger页面path: /swagger-ui.htmlenabled: true
#    tags-sorter: alpha # 标签的排序方式 alpha:按照子母顺序排序(@ApiSupport注解排序不生效,因此需要设置)
#    operations-sorter: alpha # 接口的排序方式 alpha:按照子母顺序排序(@ApiOperationSupport注解排序生效,因此这里不作设置)operations-sorter: order # 设置规则为order,该规则会使用Knife4j的增强排序扩展规则`x-order`# 启用文档,默认开启api-docs:path: /v3/api-docs    #swagger后端请求地址enabled: true
# knife4j相关配置 可以不用改
knife4j:enable: true    #开启knife4j,无需添加@EnableKnife4j注解setting:language: ZH_CN   # 中文:ZH_CN 英文:ENenable-swagger-models: trueenable-dynamic-parameter: falsefooter-custom-content: "<strong>Copyright ?? 2024 Keyidea. All Rights Reversed</strong>"enable-footer-custom: trueenable-footer: trueenable-document-manage: truedocuments: #文档补充说明- name: MarkDown语法说明locations: classpath:static/markdown/grammar/*group: 01-系统接口 # 此处分组必须使用在Knife4jConfig已存在的分组名group,当存在displayName时,使用displayName名称- name: 补充文档locations: classpath:static/markdown/others/*group: 01-系统接口 # 此处分组必须使用在Knife4jConfig已存在的分组名group,当存在displayName时,使用displayName名称

说明:使用knife4j.documents配置补充文档时,需要注意,文档格式必须为markdown格式,另外,文件存放位置如下

Knife4j补充文档存放位置

实际呈现如下

补充文档

三、Knife4jConfig配置

StatusCode类见附录A

package cn.keyidea.common.config;import cn.keyidea.common.constant.StatusCode;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.Paths;
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 io.swagger.v3.oas.models.media.Content;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.responses.ApiResponses;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.Map;/*** Knife4jConfig* 注意:分组名称暂时只能使用英文或数字,在4.0.0~4.5.0的Knife4j版本中使用中文分组会出现页面访问异常* <p>* 解决:(2024-06-18)保持原有分组名group不变,新增displayName中文名称;* 特别注意:设置displayName后,knife4j.documents.name配置文档时,需使用displayName名称* <p>** @author qyd* @date 2024-04-13*/
@Configuration
public class Knife4jConfig {private final static Logger logger = LoggerFactory.getLogger(Knife4jConfig.class);private static final String SERVICE_URL = "http://127.0.0.1:7004/tj4/doc.html";private static final String API_INFO_TITLE = "软件接口文档";private static final String API_INFO_VERSION = "V1.0";private static final String API_INFO_DESCRIPTION = "Api接口列表";private static final String API_INFO_LICENSE = "2024年度内部文档,违拷必究.";// 2024集同接口@Beanpublic GroupedOpenApi api4() {return GroupedOpenApi.builder().group("04-2024-api").displayName("04-2024集同接口").packagesToScan("cn.keyidea.second")// 自定义全局响应码.addOpenApiCustomizer((this::setCustomStatusCode)).build();}// 2023集同接口@Beanpublic GroupedOpenApi api3() {return GroupedOpenApi.builder().group("03-2023-api").displayName("03-2023集同接口").packagesToScan("cn.keyidea.control")// 自定义全局响应码.addOpenApiCustomizer((this::setCustomStatusCode)).build();}// 业务接口@Beanpublic GroupedOpenApi api2() {return GroupedOpenApi.builder().group("02-business-api").displayName("02-业务接口").packagesToScan("cn.keyidea.business")// .pathsToMatch("/v1/**").addOpenApiMethodFilter(method -> method.isAnnotationPresent(io.swagger.v3.oas.annotations.Operation.class))// 自定义全局响应码.addOpenApiCustomizer((this::setCustomStatusCode)).build();}// 系统接口@Beanpublic GroupedOpenApi api1() {// 创建了一个api接口的分组return GroupedOpenApi.builder()// 分组名称,使用英文,中文访问异常(使用displayName设置中文名,避免直接使用group设置中文时访问异常).group("01-sys-api").displayName("01-系统接口") // 使用displayName设置中文接口分组名时,group仍不可或缺.packagesToScan("cn.keyidea.sys")// 自定义全局响应码.addOpenApiCustomizer((this::setCustomStatusCode)).build();}@Beanpublic OpenAPI openAPI() {return new OpenAPI().info(new Info().title(API_INFO_TITLE).description(API_INFO_DESCRIPTION).version(API_INFO_VERSION).contact(new Contact().name("Keyidea").email("support@keyidea.cn")).license(new License().name(API_INFO_LICENSE).url(SERVICE_URL)));}/*** 设置自定义错误码** @param openApi openApi对象*/private void setCustomStatusCode(OpenAPI openApi) {if (openApi.getPaths() != null) {Paths paths = openApi.getPaths();for (Map.Entry<String, PathItem> entry : paths.entrySet()) {String key = entry.getKey();PathItem value = entry.getValue();// put方式自定义全局响应码Operation put = value.getPut();// get方式自定义全局响应码Operation get = value.getGet();// delete方式自定义全局响应码Operation delete = value.getDelete();// post方式自定义全局响应码Operation post = value.getPost();if (put != null) {put.setResponses(handleResponses(put.getResponses()));}if (get != null) {get.setResponses(handleResponses(get.getResponses()));}if (delete != null) {delete.setResponses(handleResponses(delete.getResponses()));}if (post != null) {post.setResponses(handleResponses(post.getResponses()));}}}}/*** 处理不同请求方式中的自定义响应码* - 响应码中使用原有的响应体Content(否则会造成BaseRes中通用的data无法解析各自的对象)* - 使用原生的ApiResponses作为返回体(否则会造成前端响应示例和响应内容中丢失注释)** @param responses 响应体集合* @return 返回处理后的响应体集合*/private ApiResponses handleResponses(ApiResponses responses) {// 设置默认ContentContent content = new Content();// 以下代码注释,因为无论如何都会从原生responses中获取到一个Content// MediaType mediaType = new MediaType();// Schema schema = new Schema();// schema.set$ref("#/components/schemas/BaseRes");// mediaType.setSchema(schema);// content.addMediaType("*/*", mediaType);// 从原来的responses中获取原生Contentfor (Map.Entry<String, ApiResponse> entry : responses.entrySet()) {String key = entry.getKey();ApiResponse apiResponse = entry.getValue();if (apiResponse != null) {content = apiResponse.getContent();break;}}// 获取全部全局响应自定义列表Map<Integer, String> map = StatusCode.toMap();// 设置全局响应码for (Map.Entry<Integer, String> entry : map.entrySet()) {ApiResponse api = new ApiResponse();api.setContent(content);api.description(entry.getValue());responses.addApiResponse(entry.getKey() + "", api);}return responses;}
}

四、ShiroConfig中放行Swagger相关路径

如果SpringBoot未集成Shiro,那么此处无需关注。

...
// Shiro放行swagger2(Knife4j)
// filterMap.put("/doc.html", "anon");
// filterMap.put("/swagger-resources/**", "anon");
// filterMap.put("/v2/**", "anon");
// filterMap.put("/webjars/**", "anon");// Shiro放行swagger3(Knife4j)
filterMap.put("/doc.html", "anon");
filterMap.put("/swagger-resources/**", "anon");
filterMap.put("/v3/**", "anon");
filterMap.put("/webjars/**", "anon");
filterMap.put("/swagger-ui/**", "anon");
...

五、注解更新

swagger 3 注释的包是io.swagger.v3.oas.annotations

1.原生注解更新
# Controller注解更新
@Api → @Tag
@ApiSort → @ApiSupport# 类接口注解更新
@ApiIgnore→@Parameter(hidden = true)或@Operation(hidden = true)或@Hidden
@ApiImplicitParam → @Parameter
@ApiImplicitParams → @Parameters
@ApiOperation(value = "foo", notes = "bar") → @Operation(summary = "foo", description = "bar")
@ApiResponse(code = 404, message = "foo") → @ApiResponse(responseCode = "404", description = "foo")# 实体类注解更新
@ApiModel → @Schema
@ApiModelProperty(hidden = true) → @Schema(accessMode = READ_ONLY)
@ApiModelProperty → @Schema
@ApiParam → @Parameter
2.全局替换示例
## 全局替换原有注解@Api(tags
->
@Tag(name@ApiSort(
->
@ApiSupport(order = , dataType = "Integer", dataTypeClass = Integer.class
-> 
, in = ParameterIn.DEFAULT, dataType = "String", dataTypeClass = String.class
-> 
, in = ParameterIn.DEFAULT, paramType = "path", in = ParameterIn.DEFAULT
, paramType = "path", dataType = "Integer", dataTypeClass = Integer.class
->
, in = ParameterIn.PATH, dataType = "Date", dataTypeClass = Date.class
->@ApiOperation(value
-> 
@Operation(summary@ApiImplicitParams
-> 
@Parameters@ApiModel(value | @ApiModelProperty(value
->
@Schema(name | @Schema(descriptionrequired = true | required = false (限定为entity或vo等实体类包进行更换)
->
requiredMode = Schema.RequiredMode.REQUIRED
requiredMode = Schema.RequiredMode.NOT_REQUIRED## javax注解更改(jakarta)import javax.xxx;
->
import jakarta.xxx;

六、典型应用

1.文件上传(与自定义错误码)

Knife4jConfig类中已经完美解决了全局自定义错误码,因此在单个接口中已不建议再写,除非有特殊要求。
以下接口类中自定义错误码仅为示例。

···
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
···
import org.springframework.web.multipart.MultipartFile;
···/*** 系统公共类** @author qyd* @date 2022-10-17*/
@ApiSupport(order = 1)
@Tag(name = "1-系统公共类", description = "系统公共类")
@RestController
// @RequestMapping(name = "/sys/common/", produces = MediaType.APPLICATION_JSON_VALUE)
@RequestMapping("/sys/common/")
public class CommonController {private final static Logger logger = LoggerFactory.getLogger(CommonController.class);@Autowiredprivate SysFileLogService sysFileService;@SysLogAnnotation(module = "公共类", serviceDesc = "公共类-文件上传", serviceType = ConstantsExpand.ServiceType.UPLOAD)@ApiOperationSupport(author = "qyd", order = 1)@Operation(summary = "文件上传", description = "")@Parameters({@Parameter(name = "file", description = "单文件上传", required = true, schema = @Schema(type = "file", format = "binary"), in = ParameterIn.DEFAULT),@Parameter(name = "fileType", description = "文件类型", required = true, example = "txt", in = ParameterIn.DEFAULT),@Parameter(name = "type", description = "是否使用文件原始名称:1-使用,其他-不使用(使用随机UUID)", required = false, example = "1", in = ParameterIn.DEFAULT)})@ApiResponses({@ApiResponse(responseCode = "1000", description = "响应成功"),@ApiResponse(responseCode = "1001", description = "非法字段"),})@PostMapping("uploadFile")public BaseRes uploadFile(@RequestPart(value = "file", required = true) MultipartFile file,@RequestParam(value = "fileType", required = true) String fileType,@RequestParam(value = "type", required = false) Integer type) {return sysFileService.uploadFile(file, fileType, type);}
}
2.实体类(分页参数基类PageObject
package cn.keyidea.common.bean;import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;import java.io.Serializable;/*** 分页基类 分页参数对象** @author qyd* @date 2024-06-05*/
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Schema(name = "PageObject", description = "分页对象")
public class PageObject implements Serializable {// 前端实际使用天基三期(React)写法当前页使用的是page@NotNull(message = "当前页不能为NULL")@Schema(description = "当前页,默认1", name = "page", example = "1", type = "integer", requiredMode = Schema.RequiredMode.REQUIRED)private Integer page;@NotNull(message = "分页数不能为NULL")@Schema(description = "分页数,默认15", name = "pageSize", example = "15", type = "integer", requiredMode = Schema.RequiredMode.REQUIRED)private Integer pageSize;@Schema(description = "排序字段", name = "orderBy", example = "", requiredMode = Schema.RequiredMode.NOT_REQUIRED)private String orderBy;@Schema(description = "排序方式:false-asc,true-desc", name = "desc", type = "boolean", example = "false", requiredMode = Schema.RequiredMode.NOT_REQUIRED)private Boolean desc;}
3.接口类-分页查询/新增/更新/删除/导入示例

已TLE数据的增删查改为例进行说明。

package cn.keyidea.business.controller;import cn.keyidea.business.entity.Tle;
import cn.keyidea.business.service.TleService;
import cn.keyidea.common.annotation.SysLogAnnotation;
import cn.keyidea.common.bean.BaseRes;
import cn.keyidea.common.constant.ConstantsExpand;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;/*** <p>* 卫星TLE信息表* </p>** @author qyd* @since 2022-10-12*/
@ApiSupport(order = 2)
@Tag(name = "2-卫星TLE管理", description = "卫星两行根数管理")
@RestController
@RequestMapping("/v1/tle")
public class TleController {private final static Logger logger = LoggerFactory.getLogger(TleController.class);@Autowiredprivate TleService tleService;@SysLogAnnotation(module = "TLE管理", serviceDesc = "TLE管理-分页查询", serviceType = ConstantsExpand.ServiceType.QUERY)@ApiOperationSupport(author = "qyd", order = 1)@Operation(summary = "分页查询")@Parameters({@Parameter(name = "sceneId", description = "场景ID", required = false, example = "1", in = ParameterIn.DEFAULT),@Parameter(name = "tleCode", description = "节点标识,支撑模糊查询", required = false, example = "0101", in = ParameterIn.DEFAULT),@Parameter(name = "type", description = "卫星类型:0-低轨卫星,1-中轨卫星,2-高轨卫星,3-天基用户", required = false, example = "0", in = ParameterIn.DEFAULT),@Parameter(name = "current", description = "当前页", required = true, example = "1", in = ParameterIn.DEFAULT),@Parameter(name = "pageSize", description = "分页数", required = true, example = "15", in = ParameterIn.DEFAULT)})@GetMapping("listPage")public BaseRes<BaseRes.DataList<Tle>> listPage(@RequestParam(value = "sceneId", required = false) Integer sceneId,@RequestParam(value = "tleCode", required = false) String tleCode,@RequestParam(value = "type", required = false) Integer type,@RequestParam(value = "current", required = true, defaultValue = "1") Integer pageNumber,@RequestParam(value = "pageSize", required = true, defaultValue = "15") Integer pageSize) {Page<Tle> page = new Page<>(pageNumber, pageSize);return tleService.listPage(page, sceneId, tleCode, type);}@SysLogAnnotation(module = "TLE管理", serviceDesc = "TLE管理-TLE详情", serviceType = ConstantsExpand.ServiceType.QUERY)@ApiOperationSupport(author = "qyd", order = 2)@Operation(summary = "TLE详情")@Parameter(name = "id", description = "主键ID", required = true, example = "1", in = ParameterIn.PATH)@GetMapping("getById/{id}")public BaseRes getById(@PathVariable(value = "id", required = true) Integer id) {return tleService.getOneById(id);}@SysLogAnnotation(module = "TLE管理", serviceDesc = "TLE管理-TLE新增", serviceType = ConstantsExpand.ServiceType.ADD)@ApiOperationSupport(author = "qyd", order = 3, includeParameters = {"tle.tleCode","tle.line1","tle.line2","tle.sceneId","tle.remark"})@Operation(summary = "TLE新增", description = "")@PostMapping("add")public BaseRes add(@Valid @RequestBody Tle tle) {return tleService.add(tle);}@SysLogAnnotation(module = "TLE管理", serviceDesc = "TLE管理-TLE更新", serviceType = ConstantsExpand.ServiceType.UPDATE)@ApiOperationSupport(author = "qyd", order = 4, includeParameters = {"tle.id","tle.tleCode","tle.line1","tle.line2","tle.sceneId","tle.remark"})@Operation(summary = "TLE更新")@PutMapping("update")public BaseRes update(@Valid @RequestBody Tle tle) {return tleService.update(tle);}@SysLogAnnotation(module = "TLE管理", serviceDesc = "TLE管理-TLE更新", serviceType = ConstantsExpand.ServiceType.DELETE)@ApiOperationSupport(author = "qyd", order = 5)@Operation(summary = "TLE删除", description = "")@Parameter(name = "id", description = "主键ID", required = true, example = "1", in = ParameterIn.PATH)@DeleteMapping("delete/{id}")public BaseRes delete(@PathVariable(value = "id", required = true) Integer id) {return tleService.delete(id);}@SysLogAnnotation(module = "TLE管理", serviceDesc = "TLE管理-TLE导入", serviceType = ConstantsExpand.ServiceType.IMPORT)@ApiOperationSupport(author = "qyd", order = 6)@Operation(summary = "TLE导入", description = "TLE导入数据格式请参看模板:<a href='template/txt/tle.txt'>TLE文本导入模板</a>")@Parameters({@Parameter(name = "file", description = "单文件上传", required = true, schema = @Schema(type = "file", format = "binary"), in = ParameterIn.DEFAULT),@Parameter(name = "sceneId", description = "场景ID", required = true, example = "1", in = ParameterIn.DEFAULT)})@PostMapping(value = "importTle")public BaseRes importTle(@RequestPart(value = "file", required = true) MultipartFile file,@RequestParam(value = "sceneId", required = true) Integer sceneId) {return tleService.importTle(file, sceneId);}}

七、FAQ

1.关于Controller排序说明
a) 使用tags-sorter排序问题说明

使用tags-sorter的alpha排序,是为字母排序,会造成在@Tag的name中使用00-xx/01-xx/02-xx/.../10-xxx进行说明时,排序为00-xx/10-xx/01-xx/.../09-xxx,为了对排序进行强一致,所以废弃使用tags-sorter的alpha排序,使用注解@ApiSupport进行排序定义。

b) 解决使用@ApiSupport不生效问题
  1. 移除yml中对tags-sorter的alpha排序(注释掉);
  2. 在控制器上给@ApiSupport注解,按照order值进行自定义排序,你想让哪个在前,order值就小一些,我一般是从1开始;
  3. 在注解@Tag中的description要给描述,不能是空字符串,否则@ApiSupport不生效;
  4. 弃用tags-sorter的alpha排序更改使用@ApiSupport排序后,需重启程序,此时浏览器最好清除缓存后重新访问Knife4j。
2.关于接口分组无法使用中文问题解决
a) 问题回溯

Knife4jConfig配置类中使用group进行中文分组时,会造成doc.html访问异常,推测是底层编码问题所致。

b) 解决方法
  1. Knife4jConfig配置类中,配置GroupedOpenApi时,group使用英文,displayName使用中文(doc.html最终显示displayName名称);
  2. Knife4jConfig配置类中使用displayName名称时,在yml中配置补充文档时,设置knife4j.documents.name时使用displayName名称,而不是group名称,切记!
3.关于响应内容中不出现注释内容说明

当响应内容中不出现注释时,点击右上角显示说明,触发一次关闭或勾选,即可出现注释内容。(群友推测可能是当响应结果过多时显示BUG问题,必须关闭勾选触发一次显示说明的事件)

4.关于Controller层中GET请求且接收参数为对象时的配置注意事项

参考以下两篇文章

  • Knife4j v4.0版本针对参数解析ParameterObject的问题说明
  • SpringBoot接收参数场景

集成Knife4j后,针对GET请求且接收参数为对象时,需要在yml中配置springdoc.default-flat-param-object=true;且在接受参数时使用注解@ModelAttribute

5.关于过滤参数注解@ApiOperationSupport使用

从Knife4j4.0.0开始,@ApiOperationSupport注解中的ignoreParametersincludeParameters属性不再提供支持。如果需要进行精确显示提供的参数,官方建议是新建VO类。

Knife4j4.5.0注解关于注解@ApiOperationSupport属性说明

官方说明: 3.11 过滤请求参数 | Knife4j

八、待解决问题

1.设置includeParameters无效[影响指数:5/5]【见7.5章节】

POST请求中使用includeParameters给部分对象参数时无效,界面会显示全部对象字段。

includeParameters设置部分参数仍显示全部全部

2.设置多响应码时界面显示响应状态为tab[影响指数:4/5]

接口上设置多响应码时在前端响应状态中会显示多个tab,导致导出文档时,对同一个接口会出现多次响应状态描述

设置多响应码时出现响应码Tab

附录

附录A:状态码枚举定义类(StatusCode.java)
package cn.keyidea.common.constant;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 状态码枚举定义** @author qyd* @date 2022-10-13*/
public enum StatusCode
{SUCCESS(1000, "请求成功"),INVALID_PARAM(1001, "非法字段"),SYSTEM_BUSY(1002, "系统忙"),INVALID_MASTER_KEY(1003, "无接口访问权限"),FAILURE(1004, "请求失败"),UNAUTHORIZED(1005, "未授权"),INVALID_TOKEN(2001, "TOKEN失效"),CONNECT_TIMED_OUT(3001, "请求超时"),HTTP_REQ_ERROR(3002, "HTTP请求出错");/*** 错误码*/private final int code;/*** 错误描述信息*/private final String msg;StatusCode(int code, String msg){this.code = code;this.msg = msg;}public String getMsg(){return this.msg;}public String getCode(){return this.code + "";}public int getCodeValue(){return this.code;}/*** 转为Map集合数据** @return 枚举对象Map集合*/public static Map<Integer, String> toMap(){Map<Integer, String> map = new HashMap<>(32);for (StatusCode value : StatusCode.values()){map.put(value.getCodeValue(), value.getMsg());}return map;}/*** 转为List集合数据** @return 枚举对象List集合*/public static List<Map<String, String>> toList(){List<Map<String, String>> list = new ArrayList<>(32);Map<String, String> map = null;for (StatusCode item : StatusCode.values()){map = new HashMap<>();map.put("code", item.getCode());map.put("msg", item.getMsg());list.add(map);}map = null;return list;}
}
附录B:通用响应封装类(BaseRes.java)
package cn.keyidea.common.bean;import cn.keyidea.common.constant.StatusCode;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;import java.io.Serializable;/*** 通用响应封装,范式返回(Swagger要求)** @author qyd*/
@Data
public class BaseRes<T> implements Serializable {/*** 错误码*/@Schema(name = "code", description = "错误码,当code为1000时返回正常,其余返回异常", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")public Integer code;/*** 错误提示信息*/@Schema(name = "msg", description = "错误提示信息,当code为非1000时返回提示信息", requiredMode = Schema.RequiredMode.REQUIRED, example = "请求成功")public String msg;/*** 附加返回数据*/@Schema(name = "data", description = "附加返回数据,当code为1000时返回数据")public T data;public static class DataList<T> {/*** 记录总数*/@Schema(name = "total", description = "记录总数")public Integer total;/*** 数据列表*/@Schema(name = "list", description = "数据列表")public T list;public DataList(Integer total, T list) {this.total = total;this.list = list;}}/*** 给ObjectMapper用的,代码中不要调用*/public BaseRes() {}/*** 自定义返回码和提示消息** @param code 错误码* @param msg  提示文字*/public BaseRes(int code, String msg) {this.code = code;this.msg = msg;}public BaseRes(int code, String msg, T data) {this.code = code;this.msg = msg;this.data = data;}/*** 返回成功,但是没有附加数据** @return BaseRes对象*/public static BaseRes success() {return new BaseRes(StatusCode.SUCCESS.getCodeValue(), "请求成功");}/*** 返回成功,带附加数据** @param data 附加数据* @return BaseRes对象*/public static BaseRes successData(Object data) {BaseRes value = new BaseRes(StatusCode.SUCCESS.getCodeValue(), "请求成功");value.data = data;return value;}/*** 返回参数无效响应** @return BaseRes对象*/public static BaseRes invalidParam() {return new BaseRes(StatusCode.INVALID_PARAM.getCodeValue(), "参数无效");}/*** 返回参数无效响应,自定义错误提示** @param msg 提示文字* @return BaseRes对象*/public static BaseRes invalidParam(String msg) {return new BaseRes(StatusCode.INVALID_PARAM.getCodeValue(), msg);}/*** 返回系统忙无效响应** @return BaseRes对象*/public static BaseRes systemBusy() {return new BaseRes(StatusCode.SYSTEM_BUSY.getCodeValue(), "系统忙");}/*** 返回master key无效响应** @return BaseRes对象*/public static BaseRes invalidMasterkey() {return new BaseRes(StatusCode.INVALID_MASTER_KEY.getCodeValue(), "没有接口访问权限");}/*** 返回失败,附带说明** @return BaseRes对象*/public static BaseRes fail(String msg) {return new BaseRes(StatusCode.FAILURE.getCodeValue(), msg);}/*** 返回错误信息时,仍然返回数据** @param data 数据集* @param msg  错误信息* @return BaseRes对象*/public static BaseRes failData(Object data, String msg) {return new BaseRes(StatusCode.FAILURE.getCodeValue(), msg, data);}/*** 登录失效的错误** @return BaseRes对象*/public static BaseRes invalidToken() {return new BaseRes(StatusCode.INVALID_TOKEN.getCodeValue(), "请先登录");}/*** 检查响应处理是否成功** @return 成功返回true,否则false*/@JsonIgnorepublic boolean isSuccess() {return (this.code.equals(StatusCode.SUCCESS.getCodeValue()));}/*** 返回分页列表数据** @param total 记录总数* @param list  列表数据* @return rsp*/public static BaseRes list(long total, Object list) {DataList data = new DataList((int) total, list);return BaseRes.successData(data);}
}

参考

以下参考截止[2024-06-20],CSDN等网站链接均能查看全部文章。

官方
  • 3.1 增强模式 | Knife4j
接口排序问题
  • knife4j 4.3.0版本,@ApiSupport、@ApiSort排序不会自动生成x-order扩展属性 · Issue #I7U2I0 · 萧明/knife4j - Gitee.com【解决了类Controller排序问题】

  • knife4j 中接口分组排序的方法_knife4j 接口排序-CSDN博客

SpringBoot2.x升级至3.x相关
  • SpringBoot2.7升级项目到Springboot3.1踩坑指南(jdk17/jdk21)_升级到 jdk17 springboot3.1
  • Spring Boot 3 之SpringBoot 版本升级最佳实践指南
  • 记录从SpringBoot2.x升级到SpringBoot3.x的心得
  • SpringBoot2.7升级到3.0的实践分享 - 踩刀诗人
  • 记录SpringBoot2.7.5升级SpringBoot3.0.0问题_springboot2.7升级3.0
  • Springboot3.0升级填坑-腾讯云开发者社区-腾讯云
  • JeecgBoot 框架升级至 Spring Boot3 的实战步骤-腾讯云开发者社区-腾讯云
  • How to prevent logback from outputting its own status at the start of every log when using a layout - Stack Overflow
  • Spring Boot3.0升级,踩坑之旅,附解决方案(二) - 掘金
MyBatis Plus升级相关
  • springboot3.2 整合 mybatis-plus_java.lang.illegalargumentexception: invalid value【解决引入最新MP依赖报错问题】
Knife4j升级相关
  • 关于SpringBoot2.7.18升级到3.2.x后的Knife4j使用的系列问题汇总(已全部解决) · Issue #775 · xiaoymin/knife4j
  • SpringBoot3整合Knife4j4.x版本(Swagger3、OpenApi3)_knife4j openapi3【有解决单文件多文件示例等】
  • SpringBoot 整合 knfe4j ,使用 OpenAPI3 规范_knife4j-openapi3
  • springboot3.2集成knife4j_springboot3.2 knife4j
  • SpringBoot3整合Knife4j_springboot3 knife4j
  • SpringBoot3中Swagger整合knife4j和springdoc的配置说明
  • SpringBoot3.x版本将swagger2.0升级到swagger3.0,使用knife4j-openapi3-jakarta-spring-boot-starter依赖
  • SpringBoot 使用 OpenAPI3 规范整合 knife4j的详细过程
  • Swagger系列:SpringBoot3.x中使用Knife4j - Code技术分享
  • SpringBoot3登录拦截器导致不能正常访问knife4j_knife4j放行后被拦截
  • Knife4j文档请求异常(基于SpringBoot3,查找原因并解决)
  • SpringBoot整合knife4j_knife4j集成springboot
  • SpringBoot 3.0整合OpenAPI使用教程_knife4j-openapi3-jakarta-spring-boot-starter
  • knife4j文档请求异常 · Issue #749 · xiaoymin/knife4j【Knife4j纯yml配置参考】
涉及Redis相关
  • Springboot从2.x升级到3.x以后redis默认配置调整-阿里云开发者社区
  • SpringBoot 2.x / 3.x 整合 Redis ( RedisTemplate 操作五大常用数据类型)
其他【启动告警解决】
  • Spring源码系列:BeanDefinition源码解析 - 掘金
  • 解决is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) - 神一样的存在 - 博客园
  • 解决:is not eligible for getting processed by all BeanPostProcessors-CSDN博客

最后编辑于:2024-12-09 22:14:37

著作权归作者所有,转载或内容合作请联系作者

喜欢的朋友记得点赞、收藏、关注哦!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/31378.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【氮化镓】高输入功率应力诱导的GaN 在下的退化LNA退化

2019年,中国工程物理研究院电子工程研究所的Tong等人基于实验与第一性原理计算方法,研究了Ka波段GaN低噪声放大器(LNA)在高输入功率应力下的退化机制。实验结果表明,在27 GHz下施加1 W连续波(CW)输入功率应力后,LNA的增益下降约1 dB,噪声系数(NF)增加约0.7 dB。进一…

【设计模式】掌握建造者模式:如何优雅地解决复杂对象创建难题?

概述 将一个复杂对象的构建与表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。 分离了部件的构造(由Builder来负责)和装配(由Director负责)。 从而可以构造出复杂的对象。这个模式适用于&#xff1a;某个对象的构建过程复杂的情况。 由于实现了构建和装配的解耦。…

golang从入门到做牛马:第三篇-Go程序的“骨骼架构”

在编程的世界里,Go语言就像一位优雅的舞者,它的每一个动作都简洁而高效。而要真正领略Go语言的魅力,我们得先从它的基本结构开始。Go程序的结构清晰、逻辑严谨,就像一座精心设计的建筑,每一部分都有其独特的功能和位置。接下来,就让我们一起拆解Go程序的“骨骼架构”,看…

数据结构——哈希表的实现

目录 1 哈希概念 1.1 直接定址法 1.2 哈希冲突 1.3 负载因子 1.4 将关键字转成整数 1.5 哈希函数 1.5.1 除法散列法 / 除留余数法 1.5.2 乘法散列法 1.5.3 全域散列法 1.5.4 其他方法 1.6 处理哈希冲突 1.6.1 开放地址法 1.6.1.1 线性探测 ​编辑 1.6.1.2 二次探测 1.6.…

解决AWS EC2实例无法使用IAM角色登录AWS CLI

问题背景 有时&#xff0c;我们希望一台AWS EC2实例&#xff0c;即云服务器&#xff0c;能够使用AWS CLI访问AWS管理控制台资源。 例如&#xff0c;这里&#xff0c;我们想让它能够列出所有IAM用户组。 aws iam list-groups于是&#xff0c;我们使用下面的命令&#xff0c;在…

文件系统调用─── linux第17课

目录 linux 中man 2和man 3的区别 文件内容介绍 C语言文件接口 示例: 输出信息到显示器&#xff0c;你有哪些方法 总结: 系统文件I/O 文件类的系统调用接口介绍 示例 open 函数具体使用哪个,和具体应用场景相关&#xff0c; write read close lseek ,类比C文件相关接…

【Go学习实战】03-2-博客查询及登录

【Go学习实战】03-2-博客查询及登录 读取数据库数据初始化数据库首页真实数据分类查询分类查询测试 文章查询文章查询测试 分类文章列表测试 登录功能登录页面登录接口获取json参数登录失败测试 md5加密jwt工具 登录成功测试 文章详情测试 读取数据库数据 因为我们之前的数据都…

警惕AI神话破灭:深度解析大模型缺陷与禁用场景指南

摘要 当前AI大模型虽展现强大能力&#xff0c;但其本质缺陷可能引发系统性风险。本文从认知鸿沟、数据困境、伦理雷区、技术瓶颈四大维度剖析大模型局限性&#xff0c;揭示医疗诊断、法律决策等8类禁用场景&#xff0c;提出可信AI建设框架与用户防护策略。通过理论分析与实操案…

Android 源码下载以及编译指南

和你一起终身学习&#xff0c;这里是程序员Android 经典好文推荐&#xff0c;通过阅读本文&#xff0c;您将收获以下知识点: 一、下载AOSP前的准备二、国内网络下 clone 清华大学开源软件镜像三、编写Python脚本&#xff0c;开始下载android 源码四、源码下载工具包五、编译 一…

Windows 远程桌面多端口访问,局域网虚拟IP映射多个Windows 主机解决方案

情景 项目现场4G路由局域网中两台主机通过VPN连接到公司内网&#xff0c;实现远程管理&#xff0c;要求映射两个Windows 桌面进行管理。 目录 情景 网络 思路 已知 问题解决 1.客户端通过VPN进入内网路由器配置NAT 2.使用远程主机远程桌面功能&#xff1a;IP端口号访问 …

子数组问题——动态规划

个人主页&#xff1a;敲上瘾-CSDN博客 动态规划 基础dp&#xff1a;基础dp——动态规划-CSDN博客多状态dp&#xff1a;多状态dp——动态规划-CSDN博客 目录 一、解题技巧 二、最大子数组和 三、乘积最大子数组 四、最长湍流子数组 五、单词拆分 一、解题技巧 区分子数组&…

数据结构(蓝桥杯常考点)

数据结构 前言&#xff1a;这个是针对于蓝桥杯竞赛常考的数据结构内容&#xff0c;基础算法比如高精度这些会在下期给大家总结 数据结构 竞赛中&#xff0c;时间复杂度不能超过10的7次方&#xff08;1秒&#xff09;到10的8次方&#xff08;2秒&#xff09; 空间限制&#x…

使用Modelsim手动仿真

FPGA设计流程 在设计输入之后,设计综合前进行 RTL 级仿真,称为综合前仿真,也称为前仿真或 功能仿真。前仿真也就是纯粹的功能仿真,主旨在于验证电路的功能是否符合设计要求,其特点是不考虑电路门延迟与线延迟。在完成一个设计的代码编写工作之后,可以直接对代码进行仿真,…

化工厂防爆气象站:为石油化工、天然气等领域提供安全保障

【TH-FB02】在石油化工、天然气等高危行业中&#xff0c;安全生产是至关重要的。这些行业常常面临着易燃易爆、有毒有害等潜在风险&#xff0c;因此&#xff0c;对气象条件的监测和预警显得尤为重要。化工厂防爆气象站作为一种专门设计用于这些特殊环境的气象监测设备&#xff…

Mysql InnoDB 行格式解析

该篇是学习笔记&#xff0c;笔记来源于《MySQL是怎样运行的&#xff1a;从根儿上理解MySQL》 InnoDB 是一个将表中的数据存储到磁盘上的存储引擎&#xff0c;所以即使关机后重启我们的数据还是存在的。而真正处理数据的过程是发生在内存中的&#xff0c;所以需要把磁盘中的数据…

【测试框架篇】单元测试框架pytest(4):assert断言详解

一、前言 用例三要素之一就是对预期结果的断言。 何为断言&#xff1f;简单来说就是实际结果和期望结果去对比&#xff0c;符合预期就测试pass&#xff0c;不符合预期那就测试 failed。断言内容就是你要的预期结果。断言包含对接口响应内容做断言、也包含对落DB的数据做断言。…

基础玩转物联网-4G模块如何快速实现与MQTT服务器通信

目录 1 前言 2 环境搭建 2.1 硬件准备 2.2 软件准备 2.3 硬件连接 2.4 检查驱动 3 连接MQTT服务器 3.1 创建MQTT监听Topic 3.2 打开配置工具读取基本信息 3.3 设置连接参数进行数据交互 4 总结 1 前言 MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;是一种轻…

平面机械臂运动学分析

平面机械臂运动学分析 一 整体概述1 研究步骤&#xff1a; 二 正向1 几何分析2 matlab 仿真模拟&#xff08;1&#xff09;实现效果&#xff08;2&#xff09;matlab代码&#xff1a; 3 DH矩阵计算法&#xff08;1&#xff09;计算公式&#xff08;2&#xff09;计算结果验证&a…

Hadoop、Hive、Spark的关系

Part1&#xff1a;Hadoop、Hive、Spark关系概览 1、MapReduce on Hadoop 和spark都是数据计算框架&#xff0c;一般认为spark的速度比MR快2-3倍。 2、mapreduce是数据计算的过程&#xff0c;map将一个任务分成多个小任务&#xff0c;reduce的部分将结果汇总之后返回。 3、HIv…

1.4 单元测试与热部署

本次实战实现Spring Boot的单元测试与热部署功能。单元测试方面&#xff0c;通过JUnit和Mockito等工具&#xff0c;结合SpringBootTest注解&#xff0c;可以模拟真实环境对应用组件进行独立测试&#xff0c;验证逻辑正确性&#xff0c;提升代码质量。具体演示了HelloWorld01和H…