第25章 告别丑陋与低效!Spring Boot 整合 Knife4j,让你的 API 文档颜值与实力并存!

0 阅读6分钟

告别丑陋与低效!Spring Boot 整合 Knife4j,让你的 API 文档颜值与实力并存!

拒绝手工维护文档,让接口文档在代码写完的瞬间自动"新鲜出炉"

在后端开发的世界里,有一句"谎言"流传甚广:"我下次一定补接口文档。"

在前后端分离开发的今天,接口文档就像是一座桥梁。然而,传统的 Swagger UI 虽然解决了自动生成的问题,但它的界面实在是一言难尽,面对成百上千个接口,不仅查找困难,返回的 JSON 数据也无法折叠,调试起来极其痛苦。

如果说原生 Swagger 是一把"水果刀",那么我们今天要介绍的 Knife4j 就是一把功能强悍的"瑞士军刀"。

一、为什么我们要放弃"手工织布"?

你是否经历过以下场景:

  1. Postman 请求满天飞:前端给你发一个 Postman 链接,后端给一个 JSON 串,两个人对着半天发现参数对不上。
  2. Word 文档版本错乱:接口文档从 V1.0 改到 V5.3_final_final(真的不改了).doc
  3. 联调全靠吼:"你 Swagger 地址给我一下?""哦,那个地址我看不清,你本地启动了吗?"

这一切的根源,在于文档与代码的不同步。

Knife4j(曾用名 swagger-bootstrap-ui)正是为了解决这些痛点而生。它不仅是一个 UI 增强工具,更是一套完整的 Java API 文档解决方案。

二、Knife4j:Swagger 的"究极进化体"

与丑小鸭般的原生 Swagger UI 相比,Knife4j 带来了哪些颠覆性的体验?

  • 颜值即正义:界面采用左右布局,清爽简洁,支持深色模式(部分版本),不再像原生界面那样简陋得像是 90 年代的调试工具。
  • 丝滑的调试体验:支持 Json 参数格式化、返回数据折叠。当接口返回几百行数据时,你可以像在 Chrome 开发者工具里一样,轻松折叠对象节点。
  • 强大的搜索功能:接口多了找不到?Knife4j 提供了接口搜索功能,输入方法名或路径,一键直达。
  • 文档即产品:支持在线调试,支持 Markdown、HTML、Word 格式的离线文档导出,方便交付给客户进行验收。
  • 全局参数托管:配合 JWT 等 Token 鉴权,只需设置一次全局 Header(如 Authorization),即可调试所有需要登录的接口,无需每个接口都手动输入 Token。

三、实战:Spring Boot 极速整合 Knife4j

都说它好,到底有多好?只需要 3 分钟,你就能在自己的 Spring Boot 项目中体验到它的魅力。

第一步:引入 Maven 依赖

Knife4j 提供了官方 Starter,无需再单独引入 Swagger 依赖,它会帮你自动搞定一切。

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
    <version>4.1.0</version> <!-- 建议使用最新版本 -->
</dependency>

第二步:配置 Swagger(Java Config)

创建一个配置类,像以前一样定义接口分组和基本信息。

package com.cbitedu.springboot.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.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;

@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfiguration {

    @Bean(value = "defaultApi2")
    public Docket defaultApi2() {
        Docket docket=new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(new ApiInfoBuilder()
                        //.title("swagger-bootstrap-ui-demo RESTful APIs")
                        .description("# swagger-bootstrap-ui-demo RESTful APIs")
                        .termsOfServiceUrl("http://www.xx.com/")
                        .contact("xx@qq.com")
                        .version("1.0")
                        .build())
                //分组名称
                .groupName("2.X版本")
                .select()
                //这里指定Controller扫描包路径
                .apis(RequestHandlerSelectors.basePackage("com.cbitedu.springboot.controller"))
                .paths(PathSelectors.any())
                .build();
        return docket;
    }
}

第三步:享受成果

什么都不用改!你之前的 @Api@ApiOperation@ApiModel 注解全部有效。 例如:用户controller

package com.cbitedu.springboot.controller;

import com.cbitedu.springboot.entity.UserVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@RestController
@RequestMapping("/users")
@Api(tags = "用户管理API接口")
public class UserController {

    @GetMapping("/list")
    @ApiOperation(value = "查询用户列表", notes = "目前仅仅是作为测试,所以返回用户全列表")
    public List<UserVO> list() {
        // 查询列表
        List<UserVO> result = new ArrayList<>();
        result.add(new UserVO().setId(1).setUsername("zhangsan"));
        result.add(new UserVO().setId(2).setUsername("lisi"));
        result.add(new UserVO().setId(3).setUsername("wangwu"));
        // 返回列表
        return result;
    }

    @GetMapping("/get")
    @ApiOperation("获得指定用户编号的用户")
    @ApiImplicitParam(name = "id", value = "用户编号", paramType = "query", dataTypeClass = Integer.class, required = true, example = "1024")
    public UserVO get(@RequestParam("id") Integer id) {
        // 查询并返回用户
        return new UserVO().setId(id).setUsername(UUID.randomUUID().toString());
    }

    @ApiOperation(value = "创建用户", notes = "根据User对象创建用户")
    @ApiImplicitParam(name = "user", value = "用户详细实体user", required = true, dataType = "User")
    @PostMapping("/add")
    public void add() {
        System.out.println("创建用户");
    }


    @ApiOperation(value = "更新用户详细信息", notes = "根据url的id来指定更新对象,并根据传过来的user信息来更新用户详细信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long"),
            @ApiImplicitParam(name = "user", value = "用户详细实体user", required = true, dataType = "User")
    })
    @RequestMapping(value = "/update/{id}", method = RequestMethod.PUT)
    public void update(@PathVariable Integer id) {
        System.out.println("更新用户详细信息");
    }

    @PostMapping("/delete")
    @ApiOperation(value = "删除指定用户编号的用户")
    @ApiImplicitParam(name = "id", value = "用户编号", paramType = "query", dataTypeClass = Integer.class, required = true, example = "1024")
    public Boolean delete(@RequestParam("id") Integer id) {
        // 删除用户记录
        Boolean success = false;
        // 返回是否更新成功
        return success;
    }
}

实体类

package com.cbitedu.springboot.entity;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

@ApiModel("用户 VO")
public class UserVO {

    @ApiModelProperty(value = "用户编号", required = true, example = "1024")
    private Integer id;
    @ApiModelProperty(value = "账号", required = true, example = "yudaoyuanma")
    private String username;

    public Integer getId() {
        return id;
    }

    public UserVO setId(Integer id) {
        this.id = id;
        return this;
    }

    public String getUsername() {
        return username;
    }

    public UserVO setUsername(String username) {
        this.username = username;
        return this;
    }

}

启动你的 Spring Boot 应用,打开浏览器,输入:

http://localhost:8080/doc.html

注意:是 doc.html,不再是以前的 swagger-ui.html 了! image.png

映入眼帘的将是一个功能强大、界面友好的文档中心。你可以在这里直接点击接口进行在线测试,Knife4j 甚至会为你生成请求示例。

四、那些让你爱不释手的"骚操作"

1. 解决 401 未授权问题(全局参数设置)

现在的项目几乎都有 Token 验证。在 Knife4j 页面右上角,点击 "全局参数" 或者 "授权" 按钮,添加 Key 为 Authorization,Value 为 Bearer your-token 的参数。从此,调试所有接口都不需要再手动填 Token 了。

2. 文档导出

在页面右上角找到"离线文档"按钮,可以选择 Markdown 格式。导出后直接发给前端,前端可以直接用 Typora 打开,格式清晰,随时可查,不用再写 Word 了。

五、写在最后

在敏捷开发的时代,效率就是生命

Knife4j 不仅仅是一个"皮肤",它改变了后端开发维护文档的习惯。它让写文档这件事变得无感:你写 Swagger 注解的过程,就是写文档的过程。

如果你还在忍受原生 Swagger 的卡顿和简陋,如果你还在为前后端联调时的沟通成本发愁,不妨花 5 分钟时间,把 Knife4j 引入你的 Spring Boot 项目。

让接口文档重新变得优雅,让你的代码不止于实现功能,更是一份完美的说明书。