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
# 启用swaggerUI
swagger-ui:
#自定义swagger前端请求路径,输入http:127.0.0.1:8080/swagger-ui.html会自动重定向到swagger页面
path: /swagger-ui.html
enabled: 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 英文:EN
enable-swagger-models: true
enable-dynamic-parameter: false
footer-custom-content: "<strong>Copyright ?? 2024 Keyidea. All Rights Reversed</strong>"
enable-footer-custom: true
enable-footer: true
enable-document-manage: true
documents: #文档补充说明
- 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集同接口
@Bean
public GroupedOpenApi api4() {
return GroupedOpenApi.builder()
.group("04-2024-api")
.displayName("04-2024集同接口")
.packagesToScan("cn.keyidea.second")
// 自定义全局响应码
.addOpenApiCustomizer((this::setCustomStatusCode))
.build();
}
// 2023集同接口
@Bean
public GroupedOpenApi api3() {
return GroupedOpenApi.builder()
.group("03-2023-api")
.displayName("03-2023集同接口")
.packagesToScan("cn.keyidea.control")
// 自定义全局响应码
.addOpenApiCustomizer((this::setCustomStatusCode))
.build();
}
// 业务接口
@Bean
public 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();
}
// 系统接口
@Bean
public GroupedOpenApi api1() {
// 创建了一个api接口的分组
return GroupedOpenApi.builder()
// 分组名称,使用英文,中文访问异常(使用displayName设置中文名,避免直接使用group设置中文时访问异常)
.group("01-sys-api")
.displayName("01-系统接口") // 使用displayName设置中文接口分组名时,group仍不可或缺
.packagesToScan("cn.keyidea.sys")
// 自定义全局响应码
.addOpenApiCustomizer((this::setCustomStatusCode))
.build();
}
@Bean
public 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) {
// 设置默认Content
Content 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中获取原生Content
for (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(description
required = 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);
@Autowired
private 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);
@Autowired
private 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
不生效问题
- 移除yml中对tags-sorter的alpha排序(注释掉);
- 在控制器上给
@ApiSupport
注解,按照order值进行自定义排序,你想让哪个在前,order值就小一些,我一般是从1开始; - 在注解
@Tag
中的description要给描述,不能是空字符串,否则@ApiSupport
不生效; - 弃用tags-sorter的alpha排序更改使用
@ApiSupport
排序后,需重启程序,此时浏览器最好清除缓存后重新访问Knife4j。
2.关于接口分组无法使用中文问题解决
a) 问题回溯
在Knife4jConfig
配置类中使用group进行中文分组时,会造成doc.html访问异常,推测是底层编码问题所致。
b) 解决方法
Knife4jConfig
配置类中,配置GroupedOpenApi时,group使用英文,displayName使用中文(doc.html最终显示displayName名称);- 当
Knife4jConfig
配置类中使用displayName名称时,在yml中配置补充文档时,设置knife4j.documents.name
时使用displayName名称,而不是group名称,切记!
3.关于响应内容中不出现注释内容说明
当响应内容中不出现注释时,点击右上角显示说明
,触发一次关闭或勾选,即可出现注释内容。(群友推测可能是当响应结果过多时显示BUG问题,必须关闭勾选触发一次显示说明的事件)
4.关于Controller层中GET请求且接收参数为对象时的配置注意事项
参考以下两篇文章
集成Knife4j后,针对GET请求且接收参数为对象时,需要在yml中配置
springdoc.default-flat-param-object=true
;且在接受参数时使用注解@ModelAttribute
。
5.关于过滤参数注解@ApiOperationSupport
使用
从Knife4j4.0.0开始,@ApiOperationSupport
注解中的ignoreParameters
和includeParameters
属性不再提供支持。如果需要进行精确显示提供的参数,官方建议是新建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
*/
@JsonIgnore
public 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等网站链接均能查看全部文章。
官方
接口排序问题
-
knife4j 4.3.0版本,@ApiSupport、@ApiSort排序不会自动生成x-order扩展属性 · Issue #I7U2I0 · 萧明/knife4j - Gitee.com【解决了类Controller排序问题】
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升级相关
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相关
其他【启动告警解决】
- 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
著作权归作者所有,转载或内容合作请联系作者
喜欢的朋友记得点赞、收藏、关注哦!!!
原文地址:https://blog.csdn.net/m0_74825634/article/details/145202255
免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!