关于knife4j

Knife4j的前身是swagger-bootstrap-ui,前身swagger-bootstrap-ui是一个纯swagger-ui的ui皮肤项目

一开始项目初衷是为了写一个增强版本的swagger的前端ui,但是随着项目的发展,面对越来越多的个性化需求,不得不编写后端Java代码以满足新的需求,在swagger-bootstrap-ui的1.8.5~1.9.6版本之间,采用的是后端Java代码和Ui都混合在一个Jar包里面的方式提供给开发者使用.这种方式虽说对于集成swagger来说很方便,只需要引入jar包即可,但是在微服务架构下显得有些臃肿。

因此,项目正式更名为knife4j,取名knife4j是希望她能像一把匕首一样小巧,轻量,并且功能强悍,更名也是希望把她做成一个为Swagger接口文档服务的通用性解决方案,不仅仅只是专注于前端Ui前端.

swagger-bootstrap-ui的所有特性都会集中在knife4j-spring-ui包中,并且后续也会满足开发者更多的个性化需求.

主要的变化是,项目的相关类包路径更换为com.github.xiaoymin.knife4j前缀,开发者使用增强注解时需要替换包路径

后端Java代码和ui包分离为多个模块的jar包,以面对在目前微服务架构下,更加方便的使用增强文档注解(使用SpringCloud微服务项目,只需要在网关层集成UI的jar包即可,因此分离前后端)

knife4j沿用swagger-bootstrap-ui的版本号,第1个版本从1.9.6开始,关于使用方法,请参考文档(摘自 knife4j 官方介绍)。

引入knife4j

knife4j 主要的版本基本如下所示

版本说明
1.9.6蓝色皮肤风格,开始更名,增加更多后端模块
2.0~2.0.5Ui重写,底层依赖的springfox框架版本是2.9.2
2.0.6~层springfox框架版本升级知2.10.5,OpenAPI规范是v2
3.0~底层依赖springfox框架版本升级至3.0.3,OpenAPI规范是v3

我们引入的是3.0.3,由于3.x只发布了一个版本,稳定性可能存在一定的问题,如果你想最求稳定,那么推荐你使用 2.x,由于我这里只是demo展示,加上我自己喜欢新版本,所以我这里使用了3.0.3,提前帮大家猜猜坑。

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>3.0.3</version>
</dependency>

注意

  • knife4j 已经引入了 springfox,所以在使用的时候无需再次引入
    springfox,否则有可能会导致版本冲突,如果你在网关聚合时,必须禁用 knife4j 的增强功能。
  • 使用Knife4j2.0.6及以上的版本,Spring Boot的版本必须大于等于2.2.x

创建 Swagger 配置依赖

package com.ymy.notes.config.kinfe4j;

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;

@Configuration
@EnableSwagger2
public class Knife4jConfiguration {

    @Bean(value = "defaultApi2")
    public Docket defaultApi2() {
        String groupName="3.X版本";
        Docket docket=new Docket(DocumentationType.OAS_30)
                .apiInfo(new ApiInfoBuilder()
                        .title("这是knife4j API ")
                        .description("# 这里记录服务端所有的接口的入参,出参等等信息")
                        .termsOfServiceUrl("http://yaomaoyang.com")
                        .contact(new Contact("芮棋","http://127.0.0.1","1024065216@qq.com"))
                        .version("3.0")
                        .build())
                //分组名称
                .groupName(groupName)
                .select()
                //这里指定Controller扫描包路径
                .apis(RequestHandlerSelectors.basePackage("com.ymy.notes.controller"))
                .paths(PathSelectors.any())
                .build();
        return docket;
    }

}

这里需要注意一点,如果你使用的是 2.x,那么需要将 @EnableSwagger2 替换成 @EnableSwagger2WebMvc, 因为 @EnableSwagger2 是在 3.x 才引入的注解,并且将@EnableSwagger2WebMvc 设置为不推荐使用

配置项目名和端口信息

server:
  port: 8818
spring:
  application:
    name: notes

创建一个简单的 RESTful 接口

package com.ymy.notes.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(value = "/test")
@Api(tags = "测试swagger")
public class Knife4jTestController {

    @GetMapping(value = "/hello")
    @ApiImplicitParam(name = "name",value = "姓名",required = true)
    @ApiOperation("测试接口")
    public String test(@RequestParam("name") String name){
        return "hello "+name+", I am  meimei han";
    }

}

启动项目

如果你在启动项目的时候抛出:Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException


千万不要慌,那是因为你的 springboot 版本太高,应该是 2.6.x,由于Springfox使用的路径匹配是基于AntPathMatcher,而Spring Boot 2.6.X使用的是PathPatternMatcher,所以将MVC的路径匹配规则改成 AntPathMatcher,在配置文件中加入如下参数即可(如果没有报错,可以跳过这个环节)

spring:
  mvc:
    pathmatch:
      # Springfox使用的路径匹配是基于AntPathMatcher的,而Spring Boot 2.6.X使用的是PathPatternMatcher
      # 所以需要配置此参数
      matching-strategy: ant_path_matcher

启动成功之后,在浏览器中访问:http://127.0.0.1:8818/doc.html(ip+端口+/doc.html)。
如果看到下面的画面,说明knife4j已经配置成功。




到此为止,knife4j 已经完美的运行起来了,后端人员在自测的时候再也不需要使用 postman了,也不需要给前端单独写接口文档了,一下子就增加的自己的摸鱼时间,美滋滋。

knife4j增强功能

什么是 knife4j 的增强功能?我们在前面看到的只是 knife4j 最基础的使用方式,knife4j 还有很多强大的功能还没有展示出来,比如:i18n国际化、接口添加作责、自定义文档、访问权限控制、接口排序、到处离线文档、过滤请求参数等等,这些都是 knife4j 的增强功能,那如何开启 knife4j 的增强功能呢?

Knife4j自2.0.6版本开始,将目前在Ui界面中一些个性化配置剥离,开发者可以在后端进行配置,并且提供的knife4j-spring-boot-strater组件自动装载,开发者可以在配置文件中决定需要开启的功能。

springboot 中 knife4j的完整参数如下:

knife4j:
  enable: true
  documents:
    -
      group: 2.X版本
      name: 接口签名
      locations: classpath:sign/*
  setting:
    language: zh-CN
    enableSwaggerModels: true
    enableDocumentManage: true
    swaggerModelName: 实体类列表
    enableVersion: false
    enableReloadCacheParameter: false
    enableAfterScript: true
    enableFilterMultipartApiMethodType: POST
    enableFilterMultipartApis: false
    enableRequestCache: true
    enableHost: false
    enableHostText: 192.168.0.193:8000
    enableHomeCustom: true
    homeCustomLocation: classpath:markdown/home.md
    enableSearch: false
    enableFooter: false
    enableFooterCustom: true
    footerCustomContent: Apache License 2.0 | Copyright  2019-[浙江八一菜刀股份有限公司](https://gitee.com/xiaoym/knife4j)
    enableDynamicParameter: false
    enableDebug: true
    enableOpenApi: false
    enableGroup: true
  cors: false
  production: false
  basic:
    enable: false
    username: test
    password: 12313

knife4j 的增强功能是需要开启的,默认关闭,开启也是十分的简单,在以前的版本中,开发者需要在配置文件中手动使用@EnableKnife4j来使用增强,自2.0.6版本后,只需要在配置文件中配置knife4j.enable=true即可不在使用注解
注意:要使用Knife4j提供的增强,knife4j.enable=true必须开启。包括后面所讲解到的所有增强功能,都需要设置这个参数。

下面我来介绍以下上面的这些属性值所表达的是什么意思

属性默认值说明
knife4j.enablefalse是否开启Knife4j增强模式
knife4j.corsfalse是否开启一个默认的跨域配置,该功能配合自定义Host使用
knife4j.productionfalse是否开启生产环境保护策略,详情参考文档
knife4j.basic对Knife4j提供的资源提供BasicHttp校验,保护文档
knife4j.basic.enablefalse关闭BasicHttp功能
knife4j.basic.usernamebasic用户名
knife4j.basic.passwordbasic密码
knife4j.documents自定义文档集合,该属性是数组
knife4j.documents.group所属分组
knife4j.documents.name类似于接口中的tag,对于自定义文档的分组
knife4j.documents.locationsmarkdown文件路径,可以是一个文件夹(classpath:markdowns/*),也可以是单个文件(classpath:md/sign.md)
knife4j.setting前端Ui的个性化配置属性
knife4j.setting.enableAfterScripttrue调试Tab是否显示AfterScript功能,默认开启
knife4j.setting.languagezh-CNUi默认显示语言,目前主要有两种:中文(zh-CN)、英文(en-US)
knife4j.setting.enableSwaggerModelstrue是否显示界面中SwaggerModel功能
knife4j.setting.swaggerModelNameSwagger Models重命名SwaggerModel名称,默认
knife4j.setting.enableDocumentManagetrue是否显示界面中"文档管理"功能
knife4j.setting.enableReloadCacheParameterfalse是否在每个Debug调试栏后显示刷新变量按钮,默认不显示
knife4j.setting.enableVersionfalse是否开启界面中对某接口的版本控制,如果开启,后端变化后Ui界面会存在小蓝点
knife4j.setting.enableRequestCachetrue是否开启请求参数缓存
knife4j.setting.enableFilterMultipartApisfalse针对RequestMapping的接口请求类型,在不指定参数类型的情况下,如果不过滤,默认会显示7个类型的接口地址参数,如果开启此配置,默认展示一个Post类型的接口地址
knife4j.setting.enableFilterMultipartApiMethodTypePOST具体接口的过滤类型
knife4j.setting.enableHostfalse是否启用Host
knife4j.setting.enableHomeCustomfalse是否开启自定义主页内容
knife4j.setting.homeCustomLocation主页内容Markdown文件路径
knife4j.setting.enableSearchfalse是否禁用Ui界面中的搜索框
knife4j.setting.enableFootertrue是否显示Footer
knife4j.setting.enableFooterCustomfalse是否开启自定义Footer
knife4j.setting.footerCustomContentfalse自定义Footer内容
knife4j.setting.enableDynamicParameterfalse是否开启动态参数调试功能
knife4j.setting.enableDebugtrue启用调试
knife4j.setting.enableOpenApitrue显示OpenAPI规范
knife4j.setting.enableGrouptrue显示服务分组

以下增强功能都需要

接口添加作者

前端李雷在对接接口的时候发现接口有问题,但是不知道是谁在负责这个接口,通过层层查找,终于找到了是韩梅梅负责,这样大大的阻碍了开发的效率,所以这个时候在接口上标记对应的开发着,能让找人和背锅都能做到非常精准。

使用方式:添加注解 @ApiOperationSupport(author = "芮棋")

@GetMapping(value = "/hello")
    @ApiImplicitParam(name = "name",value = "姓名",required = true)
    @ApiOperation("测试接口")
    @ApiOperationSupport(author = "芮棋")
    public String test(@RequestParam("name") String name){
        return "hello "+name+", I am  meimei han";
    }


但是对于后端来说,在每个接口上都加上这个注解,着实有点浪费时间了,所以 knife4j 在收到反馈之后,决定在 Controller 类上增加一个注解,表示当前接口类下的所有接口都是该作者负责开发。
因此在 2.0.3 版本中新增加了 @ApiSupport 注解,该注解目前有两个属性,分别是author(作者)和order(排序)
注意:如果在controller 类上添加了@ApiSuppor 注解,并且在某个接口上也添加了 @ApiOperationSupport 注解,那么接口上的作者将会覆盖 controller 类上的作者

@RestController
@RequestMapping(value = "/test")
@ApiSupport(author = "芮棋-controller")
@Api(tags = "测试swagger")
public class Knife4jTestController

@RestController
@RequestMapping(value = "/test")
@ApiSupport(author = "芮棋-controller")
@Api(tags = "测试swagger")
public class Knife4jTestController {

    @GetMapping(value = "/hello")
    @ApiImplicitParam(name = "name",value = "姓名",required = true)
    @ApiOperation("测试接口")
    @ApiOperationSupport(author = "amy-test")
    public String test(@RequestParam("name") String name){
        return "hello "+name+", I am  meimei han";
    }

}

访问权限控制

虽然 knife4j给我们提供了很方便的在线接口文档,俗话说的好,凡事都具有两面性,有利自然也有弊,那就是在生茶环境上,也会显示出接口文档,这是非常危险的一件事情,问题如下:

  • 系统部署生产环境时,我们想屏蔽Swagger的文档功能,不管是接口或者html文档
  • 通常我们有时候需要生产环境部署后,又需要Swagger的文档调试功能,辅助开发者调试,但是存在安全隐患,没有对Swagger的资源接口过滤

Knife4j 基于 Servlet 体系提供了过滤 Filter 功能,如果开发者使用 Spring Boot 开发框架进行开发的话,只需在application.properties或者application.yml配置文件中配置相关属性即可方便的解决上面的问题,不用删除 Springfox-swagger 的 jar 包或者删除相关代码等复杂的操作,提升开发体验。

资源屏蔽

目前Springfox-Swagger以及Knife4j提供的资源接口包括如下

资源说明
/doc.htmlKnife4j提供的文档访问地址
/v2/api-docs-extKnife4j提供的增强接口地址,自2.0.6版本后删除
/swagger-resourcesSpringfox-Swagger提供的分组接口
/v2/api-docsSpringfox-Swagger提供的分组实例详情接口
/swagger-ui.htmlSpringfox-Swagger提供的文档访问地址
/swagger-resources/configuration/uiSpringfox-Swagger提供
/swagger-resources/configuration/securitySpringfox-Swagger提供

项目发布到生产环境之后,我们需要屏蔽 swagger 相关的资源,由于 Knife4j 基于 Servlet 体系提供了过滤 Filter 功能,所以就不需要我们再去造轮子了,直接使用即可。

springboot 只需要在配置文件中做如下修改即可

knife4j:
  # 开启增强配置 
  enable: true
 # 开启生产环境屏蔽
  production: true

然后重启项目

如果看到如下信息,说明资源已经屏蔽成功,但是你又不想在生产环境中屏蔽 swagger 资源,只想给一部分人使用,也是可以的,加入权限校验即可。

访问页面加权控制

针对Swagger的资源接口,Knife4j提供了简单的Basic认证功能

简单点说,指定一个用户名和密码,访问 Swagger 文档需要验证登录名和密码,验证通过之后才能正常访问。

knife4 允许开发者在配置文件(application.yml/properties)中增加一组用户名和密码。

knife4j:
  # 开启增强配置 
  enable: true
 # 开启Swagger的Basic认证功能,默认是false
  basic:
      enable: true
      # Basic认证用户名
      username: test
      # Basic认证密码
      password: 123

如果用户开启了 basic (knife4j.basic.enable = true)认证功能,但是没有指定 username 和password,那么 knife4j 提供了一组默认的用户名密码

admin/123321

配置好application.yml 文件之后,我们再次重启项目(这个时候需要将之前设置的资源屏蔽需要去掉哦)

接口排序

我们在开发中,一个 controller 中往往会存在很多的接口,这样我们在文档查找的时候就会变得很苦恼,所以 knife4j 在 @ApiOperationSupport注解中增加了 order 字段,用于接口排序。

在使用此注解之前需要开启增强功能。

package com.ymy.notes.controller;

import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(value = "/test")
@ApiSupport(author = "ruiqi-controller")
@Api(tags = "测试swagger")
public class Knife4jTestController {

    @GetMapping(value = "/hello1")
    @ApiImplicitParam(name = "name",value = "姓名",required = true)
    @ApiOperation("测试接口01")
    @ApiOperationSupport(author = "ruiqi007-test",order = 1)
    public String test01(@RequestParam("name") String name){
        return "hello "+name+", I am  meimei han";
    }

    @GetMapping(value = "/hello3")
    @ApiImplicitParam(name = "name",value = "姓名",required = true)
    @ApiOperation("测试接口03")
    @ApiOperationSupport(author = "ruiqi-test",order = 3)
    public String test03(@RequestParam("name") String name){
        return "hello "+name+", I am  meimei han";
    }

    @GetMapping(value = "/hello2")
    @ApiImplicitParam(name = "name",value = "姓名",required = true)
    @ApiOperation("测试接口02")
    @ApiOperationSupport(author = "ruiqi-test",order = 2)
    public String test02(@RequestParam("name") String name){
        return "hello "+name+", I am  meimei han";
    }
}

分组排序

分组,顾名思义,就是多个 controller 之间的排序,开发者可以通过注解实现每个controller 之间的排序,实现这个功能的注解一共有三个,具体如下:

  • @ApiSupport
  • @ApiSort
  • @Api

这三个注解是存在优先级的,也就是说,当同时使用时,只会有一个注解生效,所以在使用的时候需要特别注意。优先级规则如下:

@ApiSupport  > @ApiSort  > @Api

controller 之间的排序规则为降序,越大的排在越靠前,但是排序的最小值一定需要大于 0 。

@ApiSupport

@RestController
@RequestMapping(value = "/test")
@ApiSupport(author = "ruiqi-controller",order = 999)
@Api(tags = "测试swagger")
public class Knife4jTestController 
@ApiSort

@RestController
@RequestMapping(value = "/test")
@ApiSort(value = 999)
@Api(tags = "测试swagger")
public class Knife4jTestController 
@Api

@RestController
@RequestMapping(value = "/test")
@Api(tags = "测试swagger",position = 999)
public class Knife4jTestController

虽然 @Api 中的position 字段也能实现 controller 之间的排序,但是该字段已经被 knife4j 标记为不推荐使用,所以还是推荐使用第一种排序方式(@ApiSupport)。

请求参数缓存

我们在调试接口的时候,有的接口会有很多参数,当我们好不容易填好了所有的参数,由于我们不小心关闭了页面,下次再调试的时候发现还需要再次将参数输入一遍,心态会爆炸吧,所以 knife4j 在文档管理中增加了一个选项:开启请求参数缓存

勾选这个选项之后,你填写的参数将会被 knife4j 缓存,关闭页面也不会丢失,是不是很人性呢?

编写代码,准备数据

 @PostMapping(value = "/saveUser")
    @ApiOperation("保存用户信息")
    @ApiOperationSupport(author = "ruiqi")
    public String saveUser(@RequestBody UserDTO userDTO){
        System.out.println("前端传递的用户信息:"+ userDTO);
        return "save success";
    }
package com.ymy.notes.dto;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@ApiModel("用户信息")
@Getter
@Setter
@ToString
public class UserDTO {

    @ApiModelProperty(value = "用户名")
    private String username;

    @ApiModelProperty(value = "性别")
    private String gender;

    @ApiModelProperty(value = "手机号码")
    private String phone;
}

不过你不能高兴的太早,缓存也不是一定会生效的,我已知的这两种情况,缓存将会失效,在使用的时候需要注意哦。

  • 在字段的 @ApiModelProperty 注解中添加 example (属性的示例值)属性,那么, knife4j 将不会使用缓存,使用的是后端指定的 example 。
public class UserDTO {

    @ApiModelProperty(value = "用户名",example = "李雷")
    private String username;

    @ApiModelProperty(value = "性别",example = "男")
    private String gender;

    @ApiModelProperty(value = "手机号码",example = "18888888888")
    private String phone;
}
  • 域名发生改变时,所有缓存将会失效。

动态请求参数

作为开发,不知道大家有没有做过这种操作,接口参数并不是实体对象接收,而是Map,虽然我没这么做过,但是我见过别人这么写过,当后端程序员使用 Map 接收参数的时候,Swagger ui 会怎么展示呢?我们一起来模拟一下。

定义一个以 Map 接收参数的接口

@PostMapping(value = "/saveUserForMap")
    @ApiOperation("新增用户信息-map")
    @ApiOperationSupport(author = "ruiqi")
    private String saveUserForMap(Map<String,Object> userDTO ){
        System.out.println("前端传递的用户信息:"+ userDTO);
        return "save success";
    }

我们来看看 Swagger ui 如何展示这个 Map类型的参数

这。。。。。要是前端看到这样的接口文档,心里开始问候后端的家人了吧,如何解决这个问题呢?其实很简单,knife4j 早已为我们想到了解决方案,招到文档管理 - 个性化设置 ,勾选开启动态请求参数就能解决这个问题了。

我们现在再来看看勾选了 开启动态请求参数之后的效果

在输入了一个参数之后,参数列表会自动追加新的一行,但是参数的 key 却需要前端人员自己填写,前端有很大可能是不知道参数的key,所以还是需要找后端开发人员,虽然开启了动态参数可以增加参数的个数,但是还没没有办法解决参数值的问题,那这个怎么解决呢?那这就是另外一个增强功能了:动态请求参数添加文档注释

过滤请求参数

我们在开发过程中,经常会遇到这样的一个问题,新增和修改接口,修改接口需要传递修改的记录id,但是新增则不需要,而后端往往会将修改和新增的入参对象设置为一个对象,那么这个对象中必然会存在 id 字段,这就会对新增造成误导,前端可能百思不得其解,新增的 id 我应该怎么传呢? 总不可能去偷一个吧。

所以,knife4j 支持了请求参数的过滤(忽略),实现方式也是非常的简单,使用自定义增强注解ApiOperationSupport中的ignoreParameters属性,可以强制忽略要显示的参数

使用字段忽略之前我们得先了解一下字段忽略的规则:

  • 1.例如新增接口时,某实体类不需要显示Id,即可使用该属性对参数进行忽略.ignoreParameters={"id"}
  • 2.如果存在多个层次的参数过滤,则使用名称.属性的方式,例如 ignoreParameters={"uptModel.id","uptModel.uptPo.id"},其中uptModel是实体对象参数名称,id为其属性,uptPo为实体类,作为uptModel类的属性名称
  • 3。如果参数层级只是一级的情况下,并且参数是实体类的情况下,不需要设置参数名称,直接给定属性值名称即可
  • 4.如果实体类属性中是通过List这种数组的方式,那么过滤规则会有所不同,在属性后面需要追加一个下标[0]ignoreParameters={"uptModel.uptPo[0].id"}

实现此功能需要开启增强功能。

表单提交和 JSON 提交的格式是不一样的,所以这里需要分开讲解一下,我们先来看表单提交怎么忽略字段属性。

前置条件:我们先创建两个 DTO 用来接收前端传递二点参数

package com.ymy.notes.dto;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@ApiModel("用户信息")
@Getter
@Setter
@ToString
public class UserDTO {

    @ApiModelProperty(value = "用户id")
    private Long id;

    @ApiModelProperty(value = "用户名",example = "李雷")
    private String username;

    @ApiModelProperty(value = "性别",example = "男")
    private String gender;

    @ApiModelProperty(value = "手机号码",example = "18888888888")
    private String phone;

    @ApiModelProperty(value = "用户收货地址信息")
    private UserAddressDTO userAddressDTO;
}
package com.ymy.notes.dto;

import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class UserAddressDTO {

    @ApiModelProperty(value = "收获地址的记录id")
    private Long id;
    
    @ApiModelProperty(value = "省")
    private String province;

    @ApiModelProperty(value = "市")
    private String city;

    @ApiModelProperty(value = "区")
    private String district;

    @ApiModelProperty(value = "详细地址")
    private String addr;
}

表单提交

创建新增接口和修改接口,并且希望在添加的时候过滤掉 UserDTO 中的 id 字段 与 UserAddressDTO 中的 id 字段,因为新增用户和收获地址的时候是没有 id 信息的,只有新增完成之后才会存在 id字段。

  @ApiOperationSupport(author = "ruiqi",ignoreParameters = {"id","userAddressDTO.id"})
    @PostMapping(value = "/saveUser")
    @ApiOperation("新增用户信息-表单")
    @ApiOperationSupport(author = "ruiqi",ignoreParameters = {"id","userAddressDTO.id"})
    public String saveUser( UserDTO userDTO){
        System.out.println("前端传递的用户信息:"+ userDTO);
        return "save success";
    }


    @PostMapping(value = "/updateUser")
    @ApiOperation("编辑用户信息")
    @ApiOperationSupport(author = "ruiqi")
    public String updateUser( UserDTO userDTO){
        System.out.println("前端传递的用户信息:"+ userDTO);
        return "edit success";
    }

在过滤字段的时候,第一层我们只要填写对象中的属性名即可,但如果需要过滤第二层,根据忽略规则中的第二条,我们在 UserDTO 对象中引入 UserAddressDTO 对象:private UserAddressDTO userAddressDTO; 我们还需要忽略 UserAddressDTO 对象中的 id 属性,那么需要填上 userAddressDTO.id ,其中 userAddressDTO 要与 UserDTO 对象中的 UserAddressDTO 属性名一致。

代码写好了,我们一起来看看效果。

添加接口

这里我们看不到 UserDTO 中的 id ,同时也看不到 UserAddressDTO 中的 id,忽略成功。

修改接口

由于我们在修改的时候并没有忽略 这两个字段,所以可以正常显示,这也是没有问题的。

JSON提交

由于表单提交和 json 提交的格式存在一定的差异,所以他们忽略参数的格式也存在一定的差异,我们将原来的修改和修改接口的参数接收方式改为 json 格式并且保持和表单提交一样的忽略格式(为了看到更明显的效果,我在多忽略一个字段:"username")。

@ApiOperationSupport(author = "ruiqi",ignoreParameters = {"id","userAddressDTO.id"})
 @PostMapping(value = "/saveUser")
    @ApiOperation("新增用户信息")
    @ApiOperationSupport(author = "ruiqi",ignoreParameters = {"id","userAddressDTO.id"})
    public String saveUser(@RequestBody UserDTO userDTO){
        System.out.println("前端传递的用户信息:"+ userDTO);
        return "save success";
    }


    @PostMapping(value = "/updateUser")
    @ApiOperation("编辑用户信息")
    @ApiOperationSupport(author = "ruiqi")
    public String updateUser(@RequestBody UserDTO userDTO){
        System.out.println("前端传递的用户信息:"+ userDTO);
        return "save success";
    }

我们先来看看效果会是什么样子?是否能够成功忽略呢?

新增接口

哦吼,居然也忽略成功了,卧槽,这打脸的也太快了,UserDTO 中被忽略一个字段:id,,UserAddressDTO 对象我忽略了 id 字段,我们查看 ui 界面发现确实忽略成功了,这是怎么回事呢?具体为啥,我也不太清楚,可能是 3.x 做了升级,忽略方式和表单一致了,也许是3.x 不太稳定,出现了诡异的bug,导致了 json 格式也能这样忽略,虽然这样同样可以忽略成功,由于我不确定是高版本做了优化没有更新文档还是诡异bug导致,我还是继续介绍一下 json 格式参数的忽略格式,有可能同学们这样使用却是不可以的,我推荐还是按照下面的格式编写。

json 格式如何忽略呢?

专业说法是:实例名.属性名,以新增用户为例,我们需要过滤用户id,那么写法就是:userDTO.id其中 userDTO 为 saveUser() 的 参数名

现在我们来改造一下我们的代码

@PostMapping(value = "/saveUser")
    @ApiOperation("新增用户信息")
    @ApiOperationSupport(author = "ruiqi",ignoreParameters = {"userDTO.id","userDTO.userAddressDTO.id"})
    public String saveUser(@RequestBody UserDTO userDTO){
        System.out.println("前端传递的用户信息:"+ userDTO);
        return "save success";
    }

我们一起来看看效果

这里还有一个情况是需要注意的,如果你忽略的字段在对象的第二层,那么在请求示例中,将会看不到完成的示例代码,会缺斤少两,可能 3.x 的bug吧,所以开头我申明过, 3.x 目前可能还不够稳定,升级或者使用的同学还是需要慎重啊。

搜索API接口

在文档的右上角,Knife4j提供了文档检索的功能

那这个搜索框都支持哪些关键字搜索呢?

  • 接口地址。
  • 接口名称。
  • 接口描述。

以上三个搜索都是模糊搜索,但是,需要注意的是:目前检索功能仅对当前分组下的已经加载的接口有效,对于分组中的接口,没有加载的情况下是搜索不到的,这点需要注意,换句话说该检索功能并非是全局检索,只对当前你看到的整体所有接口列表进行检索

版本要求:knife4j 版本>2.0.1 使用此规则。

全局参数

Knife4j提供基于UI临时设置全局参数功能,例如后台全局token参数等.提供该功能主要是方便开发者进行调试

目前全局参数功能主要提供两种参数类型:query(表单)、header(请求头)

如果后端Swagger有配置全局参数,该功能可以无视

功能目录:文档管理 -> 全局参数设置

自定义主页内容

不知道大家有没有觉得 swagger 的首页有一种很丑的感觉?是不是很不想看到它呢?那么接下来这条增强功能,决定是你的福利啊,knife4j 支持开发者自己替换首页,不过目前只支持 md 格式。

需要开启增强功能

Knife4j自2.0.8版本开始,开发者可以提供一个Markdown文件来自定义显示Home主页的显示内容,通过配置yml来进行开启,配置文件如下

knife4j:
  enable: true
  setting:
    enableHomeCustom: true
    homeCustomLocation: classpath:markdown/home.md

属性说明:

  • enableHomeCustom:该属性为Boolean值,默认false,如果开发者要自定义主页内容,该选项设置为true
  • homeCustomLocation:提供一个主页的Markdown文件位置

我们先在 resources 目录下创建一个 markdown 目录,然后在 markdown 目录下创建 home.md

<center><h1>ruiqi</h1></center>

昵称:ruiqi

职业:java开发工程

开源博客地址:111

联系邮箱:1024065216@qq.com

上面是home.md 的内容,接下来我们重启项目,一起来看看效果

咦,为什么没有发生改变呢?明明已经修改了啊,别急,我们忘记了最核心的一步,所以它没有替换成功,开发者需要在创建Docket逻辑分组对象时,通过Knife4j提供的工具对象OpenApiExtensionResolver将扩展属性进行赋值。

package com.ymy.notes.config.kinfe4j;

import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
import org.springframework.beans.factory.annotation.Autowired;
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;

@Configuration
@EnableSwagger2
public class Knife4jConfiguration {

    private final OpenApiExtensionResolver openApiExtensionResolver;

    @Autowired
    public Knife4jConfiguration(OpenApiExtensionResolver openApiExtensionResolver) {
        this.openApiExtensionResolver = openApiExtensionResolver;
    }

    @Bean(value = "defaultApi2")
    public Docket defaultApi2() {
        String groupName="3.X版本";
        Docket docket=new Docket(DocumentationType.OAS_30)
                .apiInfo(new ApiInfoBuilder()
                        .title("这是knife4j API ")
                        .description("# 这里记录服务端所有的接口的入参,出参等等信息")
                        .termsOfServiceUrl("http://yaomaoyang.com")
                        .contact(new Contact("ruiqi","http://1111","10240652@qq.com"))
                        .version("3.0")
                        .build())
                //分组名称
                .groupName(groupName)
                .select()
                //这里指定Controller扫描包路径
                .apis(RequestHandlerSelectors.basePackage("com.ymy.notes.controller"))
                .paths(PathSelectors.any())
                .build()
                .extensions(openApiExtensionResolver.buildSettingExtensions());
        return docket;
    }

}

通过上面示例代码,主要步骤如下:

1、通过@Autowired注解引入Knife4j向Spring容器注入的Bean对象OpenApiExtensionResolver

2、最终在Dcoket对象构建后,通过调用Docket对象的extensions方法进行插件赋值

3、插件赋值需要调用OpenApiExtensionResolver提供的buildSettingExtensions方法,获取x-settings的增强属性

这样,我们就能看到效果了。

禁用调试

在以前的版本中,开发者如果要禁用调试功能,是通过在服务端创建UiConfiguration的实体Bean对象,配置supportMethod来达到禁用部分接口的调试,自2.0.8版本后,该属性被废弃

此功能需要开启增强模式才能使用

如果开发者需要禁用调试功能,只需要在配置文件中进行操作即可

knife4j:
  enable: true
  setting:
    enableDebug: false

属性说明:

  • enableDebug:该属性是一个Boolean值,代表是否启用调试功能,默认值为true(代表开启调试),如果要禁用调试,该值设为false

同样,此操作也需要开发者在创建Docket逻辑分组对象时,通过Knife4j提供的工具对象OpenApiExtensionResolver将扩展属性进行赋值。

package com.ymy.notes.config.kinfe4j;

import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
import org.springframework.beans.factory.annotation.Autowired;
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;

@Configuration
@EnableSwagger2
public class Knife4jConfiguration {

    private final OpenApiExtensionResolver openApiExtensionResolver;

    @Autowired
    public Knife4jConfiguration(OpenApiExtensionResolver openApiExtensionResolver) {
        this.openApiExtensionResolver = openApiExtensionResolver;
    }

    @Bean(value = "defaultApi2")
    public Docket defaultApi2() {
        String groupName="3.X版本";
        Docket docket=new Docket(DocumentationType.OAS_30)
                .apiInfo(new ApiInfoBuilder()
                        .title("这是knife4j API ")
                        .description("# 这里记录服务端所有的接口的入参,出参等等信息")
                        .termsOfServiceUrl("http://yaomaoyang.com")
                        .contact(new Contact("ruiqi","http://124649687.com","1024065216@qq.com"))
                        .version("3.0")
                        .build())
                //分组名称
                .groupName(groupName)
                .select()
                //这里指定Controller扫描包路径
                .apis(RequestHandlerSelectors.basePackage("com.ymy.notes.controller"))
                .paths(PathSelectors.any())
                .build()
                .extensions(openApiExtensionResolver.buildSettingExtensions());
        return docket;
    }

}

全部操作完成之后,重启项目看效果

调试和 open 按钮没有了。

禁用搜索框

发者如果想要禁用Ui界面中的搜索功能,需要通过增强属性进行配置,此功能需要开启增强功能。

knife4j:
  enable: true
  setting:
    enableSearch: false

属性说明:

  • enableSearch:该属性是一个Boolean值,代表是否启用搜索功能,默认值为true(代表开启搜索),如果要禁用搜索,该值设为false

同样,此操作也需要开发者在创建Docket逻辑分组对象时,通过Knife4j提供的工具对象OpenApiExtensionResolver将扩展属性进行赋值。具体的代码实现请参考禁用调试自定义主页内容,我这里就不重复了。

重启项目看效果

好了,knife4j 的介绍到这里就结束了,还有一些高级的功能,就需要大家自己慢慢的摸索了,本文大部分参考了 knife4j 的官方文档,自己写的 demo,如果觉得对您有帮助,希望留下您宝贵的一赞。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐