欢迎来到我的博客,代码的世界里,每一行都是一个故事
精雕细琢的文档体验:Spring Boot 与 Knife4j 完美交汇
前言
在代码的世界里,有时候注释不足以表达你的思想,而一份优雅的 API 文档则能够让你的代码更加生动、易读。今天,我们将探讨如何通过整合 Knife4j,为你的 Spring Boot 项目添加一把锐利的文档利器。就像在一场精彩的武术表演中,每一刀都能展现出独特的艺术魅力,Knife4j 也将为你的文档世界带来新的精彩。
Knife4j 与 Swagger 的区别
Knife4j 和 Swagger 是两个用于 API 文档生成和展示的工具,它们都基于 OpenAPI(以前称为 Swagger)规范。下面是 Knife4j 与 Swagger 的区别以及对比它们的特性和优劣:
1. 特性与优劣势对比:
Knife4j:
特性:
- UI 界面美观: Knife4j 提供了一套漂亮的、易用的 UI 界面,展示了 API 文档的信息,并支持在线调试和测试。
- 支持多种注解: Knife4j 支持众多的 Swagger 注解,并且提供了一些额外的扩展注解,如
@ApiImplicitParams
、@ApiOperationSupport
等。 - 在线调试: 提供了在线调试和测试 API 的功能,开发者可以直接在文档中进行接口的测试。
- 强大的扩展性: 支持自定义扩展,开发者可以根据需求进行定制化。
优势:
- UI 界面美观,易用性好。
- 支持丰富的 Swagger 注解,提供了更多的功能。
- 提供了在线调试功能,方便开发者测试接口。
Swagger:
特性:
- 标准化规范: Swagger 是 OpenAPI 规范的实现之一,具有广泛的支持和社区。
- 生态系统丰富: 由于是较早的 API 文档工具,有庞大的社区和丰富的插件生态系统。
- 强大的生态支持: 支持多种语言和框架,适用于各种项目。
优势:
- 作为 OpenAPI 规范的实现,与其他支持 OpenAPI 的工具和库更好地集成。
- 有着较长时间的发展历史,生态系统较为成熟。
2. 选择 Knife4j 的理由:
- UI 界面更友好: Knife4j 的 UI 界面相较于原生 Swagger 更加美观和易用,提供了更好的用户体验。
- 功能扩展更丰富: Knife4j 在 Swagger 的基础上进行了功能扩展,支持更多的 Swagger 注解和一些额外的扩展注解,提供了更多的功能。
- 在线调试更方便: Knife4j 提供了在线调试和测试 API 的功能,方便开发者在文档中直接进行接口测试。
- 社区支持良好: 尽管相对于 Swagger,Knife4j 的用户规模可能较小,但其社区仍然活跃,能够提供一定的支持。
综合考虑上述因素,选择 Knife4j 的主要理由在于其更友好的 UI 界面、丰富的功能扩展和方便的在线调试功能。然而,具体选择应该根据项目需求、开发者团队的偏好以及其他因素来决定。
Knife4j中的注解说明
Knife4j 中的注解主要用于配置和描述 API 文档。这些注解帮助开发者更精确地定义 API 接口、模型类等信息,以便生成详细的 API 文档。以下是一些在 Knife4j 中常用的注解及其作用:
1. 控制器类相关注解:
@Api
注解:
@Api
注解用于对整个控制器类进行描述,指定一些全局信息,如分组、描述等。
@Api(tags = "示例接口", description = "用于演示 Knife4j 的 API 接口")
@RestController
@RequestMapping("/api")
public class SampleController {
// ...
}
123456
tags
:指定分组,用于在文档中对接口进行分类展示。description
:对整个控制器的描述。
2. 接口方法相关注解:
@ApiOperation
注解:
@ApiOperation
注解用于对单个接口方法进行描述,指定该接口的一些信息,如标题、说明等。
@ApiOperation(value = "获取 Hello 信息", notes = "这是一个示例接口,返回 'Hello, Knife4j!'")
@GetMapping("/hello")
public String getHelloMessage() {
return "Hello, Knife4j!";
}
12345
value
:接口的标题。notes
:接口的详细说明。
@ApiParam
注解:
@ApiParam
注解用于对接口方法的参数进行描述,指定参数的一些信息,如名称、是否必须、描述等。
@GetMapping("/greet")
@ApiOperation(value = "根据名称问候", notes = "根据传入的名称返回问候语")
public String greet(@ApiParam(value = "姓名", required = true) @RequestParam String name) {
return "Hello, " + name + "!";
}
12345
value
:参数的描述。required
:指定参数是否是必须的。
3. 模型类相关注解:
@ApiModel
注解:
@ApiModel
注解用于对模型类进行描述,指定模型的一些信息,如描述、子类等。
@ApiModel(description = "用户信息")
public class User {
// ...
}
1234
description
:模型的描述。
@ApiModelProperty
注解:
@ApiModelProperty
注解用于对模型类的属性进行描述,指定属性的一些信息,如描述、示例值等。
public class User {
@ApiModelProperty(value = "用户ID", example = "123")
private Long id;
@ApiModelProperty(value = "用户姓名", example = "John Doe")
private String name;
// ...
}
12345678910
value
:属性的描述。example
:属性的示例值。
这些注解使得 Knife4j 能够生成更加详细、清晰的 API 文档。在实际应用中,结合这些注解,可以使 API 文档更具可读性和易用性。
实战演示
引入maven依赖
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
12345
配置类
package fun.todoitbo.botally.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* @author xiaobo
*/
@Configuration
@EnableSwagger2
public class Knife4jConfiguration {
@Bean(value = "defaultApi2")
public Docket defaultApi2() {
// 设置分组名称
String groupName="记账API";
// 创建 Docket 对象
Docket docket=new Docket(DocumentationType.OAS_30)
.apiInfo(new ApiInfoBuilder()
.title("记账 API ")
.description("# 关于记账软件的 API")
.contact(new Contact("一只牛博","https://todoitbo.fun","todoitbo@qq.com"))
.version("3.0")
.build())
// 设置分组名称
.groupName(groupName)
.select()
// 指定Controller扫描包路径
.apis(RequestHandlerSelectors.basePackage("fun.todoitbo.botally.controller"))
.paths(PathSelectors.any())
.build();
return docket;
}
}
123456789101112131415161718192021222324252627282930313233343536373839404142
请求vo实现
package fun.todoitbo.botally.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* 用户账单表(TbBill)实体类
*
* @author todoitbo
* @since 2024-01-03 13:57:52
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class GetTbBillReqVo {
@ApiModelProperty(value = "账单主键id")
private Long id;
@ApiModelProperty(value = "账单类别id")
private Long categoryId;
@ApiModelProperty(value = "最小金额")
private Double minAmount;
@ApiModelProperty(value = "最大金额")
private Double maxAmount;
@ApiModelProperty(value = "名称")
private String name;
@ApiModelProperty(value = "开始时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime startTime;
@ApiModelProperty(value = "结束时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime endTime;
@ApiModelProperty(value = "账单出账")
private int inBill;
@ApiModelProperty(value = "账单时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime belongTime;
}
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
响应VO实现
package fun.todoitbo.botally.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* @author todoitbo
* @date 2024/1/3
*/
@Data
public class TbBillRespVo {
@ApiModelProperty(value = "账单ID,主键")
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
@ApiModelProperty(value = "账单类别id")
@JsonSerialize(using = ToStringSerializer.class)
private Long categoryId;
@ApiModelProperty(value = "账单时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime billTime;
@ApiModelProperty(value = "金额")
private BigDecimal amount;
@ApiModelProperty(value = "名称")
private String name;
@ApiModelProperty(value = "账单类别名称")
private String categoryName;
@ApiModelProperty(value = "是否收入")
private int inBill;
}
12345678910111213141516171819202122232425262728293031323334353637383940414243
controller实现
package fun.todoitbo.botally.controller;
import fun.todoitbo.botally.dto.BoResult;
import fun.todoitbo.botally.service.ITbBillService;
import fun.todoitbo.botally.vo.DetailRespVo;
import fun.todoitbo.botally.vo.GetTbBillReqVo;
import fun.todoitbo.botally.vo.SaveTbBillReqVo;
import fun.todoitbo.botally.vo.TbBillRespVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* 用户账单表(TbBill)控制器
*
* @author todoitbo
* @since 2024-01-03 13:57:52
*/
@RestController
@RequestMapping(value = "TbBill")
@Api(tags = "用户账单表(TbBill)控制器")
@Validated
public class TbBillController {
@Resource
protected ITbBillService service;
@PostMapping("/saveBill")
@ApiOperation(value = "新增账单")
public BoResult<Boolean> saveBill(@Validated @RequestBody SaveTbBillReqVo saveTbBillReqVo) {
return BoResult.resultOk(service.saveBill(saveTbBillReqVo));
}
@DeleteMapping("/deleteBill")
@ApiOperation(value = "删除账单")
public BoResult<Boolean> deleteBill(@NotNull(message = "id不能为空") Long id) {
return BoResult.resultOk(service.deleteBill(id));
}
@GetMapping("/getBillList")
@ApiOperation(value = "获取账单列表")
public BoResult<List<TbBillRespVo>> getBillList(@Validated GetTbBillReqVo getTbBillReqVo) {
return BoResult.resultOk(service.getBillList(getTbBillReqVo));
}
@GetMapping("/getDetail")
@ApiOperation(value = "获取账单详情")
public BoResult<DetailRespVo> getDetail() {
return BoResult.resultOk(service.getDetail());
}
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
效果展示图
彩蛋(报错 解决)
这个报错是因为springboot版本高,由于现代浏览器和中间件对URL的敏感性增加,一些场景下使用ant风格的URL匹配可能会出现问题。因此,为了保证最大的兼容性和可移植性,建议在Spring Boot项目中添加这个配置。
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
1234
结语:
希望通过这篇文章,你能够更深入地了解 Knife4j 在 Spring Boot 中的应用。在文档的世界里,每一刀都是为了更好地表达思想,而 Knife4j 就是我们的得力助手。让我们一同投入这场文档的舞台,为代码增色不少,为开发带来更多的便利与乐趣。