关于 swagger 本文不再赘述,网上文章很多。本文要讲的是 Knife4j 3.0.3 整合SpringBoot 2.6.4,因为 knife4j 3.x版本(目前只有这一个版本)和2.x版本还是有一些区别的,如果配置注解方面使用不当,很容易导致文档页面打不开。同时,SpringBoot 2.6.4的版本也是相对较高的版本,在SpringBoot 2.4以上的版本和之前的版本还是不一样的,这个也容易导致一些问题。本文就这两个版本的整合做一个实战介绍。
一、引入依赖
<!-- Spring Boot 项目starter,快速使用knife4j增强文档 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
使用Knife4j 3.0.3只需要引入这一个依赖即可,其他的swagger依赖不需要了。
这是Knife4j 3.0.3版本的依赖所包含的依赖,可见,swagger的依赖已经有了。
二、代码配置
直接上代码
package com.dake.common.config;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.lang.reflect.Field;
import java.util.List;
import java.util.stream.Collectors;
/**
* Knife4j 3.X 配置类.
* 访问地址:
* <p>
* Knife4j 访问首页:<a href="http://localhost:8090/doc.html#/home">...</a>
* </p>
*
* @author fangqi
* @date 2022-6-27 23:43:39
*/
@EnableKnife4j
@Configuration
@EnableSwagger2
@Import(BeanValidatorPluginsConfiguration.class)
// 在 Swagger 3.X 以下版本报错时可以加此注解解决,但是在3.X版本以上的,加此注解会导致页面无法打开
//@EnableWebMvc
public class SwaggerConfig {
private static final String SWAGGER_TITLE = "XXX项目 API 接口文档";
private static final String VERSION = "3.0.3";
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.OAS_30)
.enable(true)
// .useDefaultResponseMessages(false)
.apiInfo(apiInfo())
.groupName("3.X 版本")
.select()
// 方式一: 配置扫描 所有想在swagger界面的统一管理接口,都必须在此包下
// .apis(RequestHandlerSelectors.basePackage("com.dake.controller"))
.apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
// 方式二: 只有当方法上有 @ApiOperation 注解时才能生成对应的接口文档
// .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(SwaggerConfig.SWAGGER_TITLE)
.description("# XXX项目API接口文档简介")
.termsOfServiceUrl("http://127.0.0.1/#/login")
.contact(new Contact("fangqi", "", "[email protected]"))
.version(SwaggerConfig.VERSION)
.build();
}
@Bean
public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(@NotNull Object bean, @NotNull String beanName) throws BeansException {
if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
}
return bean;
}
private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
List<T> copy = mappings.stream()
.filter(mapping -> mapping.getPatternParser() == null)
.collect(Collectors.toList());
mappings.clear();
mappings.addAll(copy);
}
@SuppressWarnings("unchecked")
private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
try {
Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
assert field != null;
field.setAccessible(true);
return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
};
}
}
三、配置文件
在application.yml文件中,添加如下配置:
springfox:
documentation:
swagger:
v2:
path: /api-docs
use-model-v3: true
knife4j:
# 开启增强
enable: true
# 开启登录认证
basic:
enable: true
username: admin
password: 123456
解释:
上面第一个配置是knife4j的配置,下面的配置是开启增强,打开页面时需要 输入账号密码,我们这里账号 是admin,密码是123456.
四、页面功能
浏览器输入:
http://localhost:8090/doc.html#/
输入用户名和密码即可看到我们所有的controller和加了注解的实体类。
左侧一共有四个被我们用红框框柱的页签。这个显示页面比原先的swagger的页面风格更加美观,也更加适合国人的使用习惯。
下面我们就这四个菜单一一讲解。
1.主页
目前我们 看到的就是主页的信息,也就是我们这个接口文档的简单介绍。
2.Swagger Modules
这部分就是我们在项目中定义的实体类,我们点击打开查看一下。
我们看到这个实际上是层次分明的,不同的实体类类型显示不同的 颜色,非常美观,比起原来的swagger好看多了。
我们点开第一个看看实体类的情况。
我们看到,有字段的英文名、中文名和数据类型,非常的清晰明了。
要知道,这个不是我们手写的。是打开页面直接生成的,哪怕里面的字段变化了,只要我们重启项目、刷新页面,这个接口文档会同步进行变更,使用起来非常方便。以后再和其他同事对接口文档的时候就非常方便了,给他一个地址加上用户名密码,让他自己玩去,不懂了再来问。前端的小姐姐小哥哥用起来也是贼爽的。
上面的这个实体类我们还可以给他起个中文名,那样就更好了,当然你习惯英文名也很好。
上面配置我们配置过了,代码中怎么使用呢?
五、如何使用
1.在controller上加两个注解:
@Api(tags = "XX管理")
@ApiSupport(author = "fangqi", order = 1)
上面一共2个注解,第一个是用来说明这个controller的作用,第二个是用来说明是谁开发的,其他就是为了让别人知道这是谁开发的文档,到时候方便找到你。后面的那个order指的是这个controller在左边侧边栏的展示顺序。你想啊,这么多controller,你定义一个,我定义一个,项目稍微多一些代码,找起来也没有那么方便。如果我们给他定义一个顺序,岂不是美哉?order越小,越靠前。有人说了,大家都定义成1,岂不是乱了?这个当然是的,不过不会报错,都是1,等于都没用了而已。这个顺序呢,其实由项目leader定义会更好一些。最重要的放上面,其他的按照顺序排就是了。
到这里有人说了,原来的接口是你fangqi开发的,现在呢,我要在这个上面加一个功能,也就是加一个请求的方法,到时候别人还是找你,这不合适吧!你说的没错,关于这一点我们可以用另外一个注解来单独设置你增加的那个方法的作者。
比如现在有一个queryNameById的方法是fangdake新加的,那我们只需要在这个请求方法上加这个注解就行了。到时候,其他接口还是找fangqi,而你这个方法会找fangdake。
2.方法上加注解
@ApiOperationSupport(author = "fangdake")
我们上面讲了,可以给类上加注解,给一个中文的提示,告诉使用者这个controller是干什么的,但是方法上呢?当然了,方法上我们一样可以给一个中文的说明:
@ApiOperation(value = "查询初始化数据",
notes = "新增和修改页面的初始化下拉列表数据",
tags = "延伸操作")
其中,value标签是指的方法的基本功能;notes就是一个描述性的文字,详细的说明;tags呢,其实用处不大,在打开接口文档的时候它会把你这个方法再展示一份放到你给tags起的名字的这个标签下。你打开延伸操作的时候就能看到你加过tags标签的方法了。其实有点类似于分组一样,从你原先的controller中又给出了一个分组。个人感觉呢,一般是用来给测试或者领导看的。领导觉得有一二三四五,这五个功能比较重要,他可能偶尔要看看,或者这几个方法比较重要——其实就是太难了,测试要多次测试,而且问题很多,今天测试玩明天测试。这时你就可以把不同类的不同的几个方法抽出来放一个单独的分组,这样比一个菜单一个菜单的去找方便多了,尤其是有一些方法上面用的不是GetMapping这样的注解,而是RequestMapping,到时候,每个方法都会在这个菜单下生成很多个测试接口,也就是重复的,你会很难受。
上图其实就是2个方法,但是我们打开这个菜单的时候会显示这么多。因为接口中没有指定具体的请求方式,都是RequestMapping,所以这个接口文档的测试功能就把所有的测试方法全部给展现出来了,不管是get、post还是delete等请求。
我们打开一个具体的接口。
可以看到请求参数和返回参数清晰明了。之所以会这样,是因为我们的请求参数和返回参数都是对象,而且我们在具体的对象上加了注解,所以才会如此。那如何操作呢,我们来看看。
首先是请求参数:
@Data
@ApiModel(description = "查询xxx列表DTO")
public class QueryBiListDTO {
@NotBlank(message = "页码不能为空")
@ApiModelProperty(value = "页码", required = true)
private Integer pageNo;
@NotBlank(message = "每页条数不能为空")
@ApiModelProperty(value = "每页条数", required = true)
private Integer pageCount;
@ApiModelProperty(value = "xxid")
private String id;
}
返回参数同请求参数,是一样的。
我们看到上面的那个图片,有一个调试。我们点击调试。
我们启动项目,上面输入我们需要的参数,直接可以调试了,非常方便。不用像postman一样,搞来搞去,麻烦死了,而且这个是所有人都能看到的,不用每个人都去搞一份。
六、参数设置
有人说了,postman测试的东西可以保存啊!其实这个一样的。
打开文档管理:
我们看到默认是开启请求参数缓存的,也就是说我们刚才请求过一次,下一次再请求的时候,我们刚才输入的参数还在,不用每次都输入很多参数,这么麻烦。如果是一两个字段还好,如果是新增编辑,特别是很多字段的大页面的编辑,每次搞一下,办个小时过去了。这个接口就很方便了,这次新增时你输入的值,下次还在。你测试的时候只需要改个别地方就可以了,非常非常方便。
到这了,还有人会说,我们很多时候要从请求头里面那一些东西,比如token了,userId了,等等。确实,一般是这么操作的。别慌,我们这个接口文档测试的的时候一样可以搞定。
比如,你每次需要从请求头里面获取到用户id,那么你可以这里设置成header,然后输入userId,然后是userId的具体值。如果你要输入token,那么就输入token和具体的值。至于这个token的值嘛,需要你获取到之后放到这里。
这样看来,这个接口文档就不止是接口文档了,它同时也是测试的工具,保存测试数据的工具。至于术语咱就不bb了,什么这加那加那的综合体之类的,没啥意义。
当然,如果有人想要你这个测试的数据,又不能让你一个一个的拷贝出来给他,那多难受啊!这个文档支持4种方式的下载。
七、非实体类参数设置
上面我们给出的接口文档的请求参数都是对象的,我们当然知道每个字段什么意思了,当时如果是单个字段或者是map呢?用map作为controller的请求参数的非蠢既坏,但是没办法,谁让人家是用户呢?
这个也很简单。
@ApiOperation("修改密码")
@ApiImplicitParams({
@ApiImplicitParam(name = "username",value = "账号" , dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "oldPassword",value = "旧密码" , dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "newPassword",value = "新密码" , dataType = "String", paramType = "query")
})
八、忽略参数
在我们的新增和编辑页面,经常是编辑只比新增多了一个id,如果此时我们再单独给新增编辑加一个实体类,其实有一些多余。此时我们只需要指定对应的字段忽略掉即可。
@PostMapping("addUser")
@ApiOperation("新增用户")
@ApiOperationSupport(ignoreParameters = "id") // 忽略掉User中的id属性,不显示在文档中
public void addUser(User user) {
}
注意:
ignoreParameters支持以数组形式添加多个忽略参数
ignoreParameters支持忽略级联对象的参数,比如User实体类中有个Address类型的属性addr,那么如果想要忽略Address的属性id,那么只需要配置为ignoreParameters = "addr.id"即可
如果要忽略的参数过多,可以使用includeParameters反向配置
如果是以
@RequestBody
形式接收参数,那么
ignoreParameters
中填写
参数名.要忽略的属性名
即可。
@PostMapping("addUser2")
@ApiOperation("添加用户2")
@ApiOperationSupport(ignoreParameters = {"user.id", "user.age"})
public void addUser2(@RequestBody User user) {
}
注意:
ignoreParameters支持以数组形式添加多个忽略参数
ignoreParameters支持忽略级联对象的参数,比如User实体类中有个Address类型的属性addr,那么如果想要忽略Address的属性id,那么只需要配置为ignoreParameters = "user.addr.id"即可
如果要忽略的参数过多,可以使用includeParameters反向配置
九、生产上关闭 knife4j
如果要在生产上关闭knife4j文档——生产上肯定要关闭的,只要在配置文件中配置:
# 开启屏蔽文档资源 production: true
如果你的项目中只有一个application.yml,则项目上线时会指定profile,也就是prd的环境,这个时候把屏蔽文档资源打开即可;如果是多个application文件,比如一个基础的application.yml,然后一个application-dev.yml,一个application-uat.yml,一个application-prd.yml,那么只要在prd这个文件中添加上面 的配置即可。
版权归原作者 北冥牧之 所有, 如有侵权,请联系我们删除。