springboot 整合knife4j(绝对可用)1.9.6版~2.0.8版

1.情景展示

  开发接口供别人调用或者前后端分离后,前端调后端请求需要提供什么参数,会返回什么样的结果。如果一对一沟通的话,费时费力,写接口文档的话也比较麻烦。

2.效果展示

  现在使用knife4j就可以实现以下效果:

  点击调试按钮,就可以直接配置请求参数,发送请求啦

3 1.9.6版本配置

  这是1.9最后一个版本,要想实现这种效果,实现步骤如下:

  1.引入jar包

  只需要引入这一个jar包就可以了,无需其它jar包。    

  2.创建一个配置类,比如:Knife4jConfig

  3.导包

  4.添加注解 

  以上是三个注解,一个都不能少,在这里需要注意的一点就是:

  启用knif4j使用的注解是:@EnableSwaggerBootstrapUi,而不是@EnableKnife4j

  5.创建Docket对象,并注入到spring容器当中

  6.设置主页文档内容

   在配置文件类Knife4jConfig中添加以上两个方法即可。

4 2.0.4版本配置

  导包

<   code >        
<   dependency >
    <   groupId >com.github.xiaoymin       
    <   artifactId >knife4j-spring-boot-starter       
    <   version >2.0.4       

  说明:2.0.2版本的,只需要把上面jar包的版本号改成2.0.2即可,下面的配置一模一样。

  只需要引入这一个jar包就可以了,无需其它jar包。    

  创建一个配置类

比如:Knife4jConfig(配置文件类最好创建一个config/conf包,方便统一管理)

 

        import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import io.swagger.annotations.ApiOperation;
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.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * 项目接口文档配置(swagger)
 * @description: 使用knife4j需要一个swagger的配置类
 * @author: Marydon
 * @date: 2020年08月11日 0011 9:22
 */
@Configuration
// 必须启用swagger
@EnableSwagger2
@EnableKnife4j
// swagger文档配置只在指定环境可访问(该注解的值对应的是application.yml文件中的spring.profiles.active的值)
// 支持使用!,比如:!prod表示非生产环境
// @Profile({"dev","test","jc"})
public class Knife4jConfig {
    /*
     * 创建连接的包信息
     * @param: 方法名称随便起,虽然它的名字会被作为Bean对象的名字注入spring容器
     * @date: 2020年08月11日 0011 9:51
     * @param: 
     * @return: springfox.documentation.spring.web.plugins.Docket 返回创建状况
     */
    @Bean
    public Docket defaultApi2() {
        return new Docket(DocumentationType.SWAGGER_2)         // 选择swagger2版本
                .useDefaultResponseMessages(        false )
                // 接口文档的基本信息
                .apiInfo(apiInfo())
                .select()
                // 这里指定Controller扫描包路径(项目路径也行)
                // 方式一:配置扫描:所有想要在swagger界面统一管理的接口,都必须在此包下
                // .apis(RequestHandlerSelectors.basePackage("com.company.project.web.controller.entrance"))
                // 方式二:只有当方法上有@ApiOperation注解时,才能生成对应的接口文档
                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.        class ))
                // 路径使用any风格(指定所有路径)
                .paths(PathSelectors.any())
                .build();

    }

    /*
     * 设置文档信息主页的内容说明
     * @date: 2020年08月11日 0011 9:52
     * @param:
     * @return: springfox.documentation.service.ApiInfo 文档信息
     */
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title(        "接口项目 后台服务API接口文档" )
                .description(        "服务相关接口(knife4j)" )
                // 服务Url(网站地址)
                .termsOfServiceUrl(        "http://localhost:8080/" )
                .contact(        new Contact(        "Marydon" ,        null ,        "marydon20170307@163.com" ))
                .version(        "1.0" )
                .build();
    }

}

  启动项目,访问:http://IP:端口号/程序名称/doc.html即可,显示主页。

5.注解基本用法

  @Api()

用于类的说明,准确的来说用于controller,表示这个类是一个swagger资源

  对应的是:一个Controller

  注意:tags属性必须显式声明,否者无法修改小组的显示名称

  举例:

 

   实际效果:

  未生效(因为没有声明:tags=)

  @ApiOperation()

用于方法的说明,表示这是一个HTTP请求

  对应的是controller里的方法

  2020-12-16

  这里需要注意的是:

  @PostMapping对应的post请求,反映到API上就是post请求;

  @GetMapping对应的get请求,反映到API上就是get请求;

  @RequestMapping如果没有指定具体请求方式

  API文档如下:

  它会给你生出7种不同的请求方式,API本身没错

  实际上,我们使用@RequestMapping的真实目的,也许是:想让该接口既支持get请求,也支持post请求,那我们可以做出限定。

  这样,就OK啦 

2023年9月28日15:17:04

设置请求数据类型(只是为了knife4j文档识别)

在SpringMVC和SpringBoot框架当中,控制层的参数格式,有两种经常使用的数据格式(也是前后端数据交互常用格式)。

一种是x-www-form-urlencoded(form表单),一种是application/json(JSON)。

有且只有,当入参被@RequestParam注解修饰时,才会被Knife4j文档识别成:form表单格式。

否则,会被Knife4j统一当中JSON格式的请求参数。

当然,我们不设置@RequestParam注解,后台也是能正常解析出前端发起的form表单数据的。

当入参被@RequestBody注解修饰时,会被Knife4j文档识别成:JSON格式。

设置响应数据类型(只是为了knife4j文档识别)

声明:无论是使用的SpringMVC还是SpringBoot框架,最终,响应数据的格式,默认使用的都是JSON格式

有一个奇怪的地方就是:

  knife4j生成的所有接口方法,所对应的响应数据类型都是:*/*,当我在Mapping注解里指定可接受的数据类型后,API上却成了响应数据类型的值。

  请求的数据类型是不会受到影响的,knife4j是根据方法入参来自动判断请求的数据类型的,举个栗子:

  上面我们用的是@RequestBody,那knife4j就知道请求数据类型为:application/json;

  当我们改用@RequestParam时,它会识别为:application/x-www-form-urlencoded。

  等等,上面两种是我实际测试出来的,其它的识别方式就不得而知了。

  2020-12-17

  今天,发现:knife4j会将header和produces的值都塞到响应数据类型里面。比如:

        @RequestMapping (value =         "clear" , method = {RequestMethod.GET, RequestMethod.POST}, headers = {        "Accept=text/pain" }, produces =         "text/html;charset=UTF-8" )

  所以,当我们想要这里展示出具体的响应数据类型的话,在Mapping注解添加produces属性并设值即可。  

2023年9月28日15:17:22 

@GetMapping(value = "/getPipelineOne", produces = {"application/json;charset=UTF-8"})

  @ApiOperationSupport()

(knife4j增加特性)用于接口方法排序,作者信息描述等

  @ApiImplicitParams()

参数列表说明

  如果不是用实体类接收入参的话,存在多个入参可以用该注解说明

@ApiParam()

对单个参数的说明

  如果不是用实体类接收入参的话,存在一个或者多个入参也可以用该注解进行逐个说明

2023年9月28日16:29:39

错误用法

对应的接口说明

接口的入参变成了汉字。

对参数的解释说明,应该使用@ApiParam的value属性。

一般情况下,是不需要声明@ApiParam的name属性的。因为name的默认值就是请求参数的参数名称。

示例:

@ApiParam(name = "数据来源表名(区分大小写)", required = true, example = "table1")
String tablenameSource

参数名就是:tablenameSource。

正确用法

@ApiOperation(value = "查询表字段映射列表", notes = "查询表字段映射列表", httpMethod = "GET")
@GetMapping(value = "/getFlinkDataFieldList", produces = {"application/json;charset=UTF-8"})
public List<FlinkDataField> getFieldList(@ApiParam(value = "任务主键ID", required = true, example = "1")
                                         @Pattern(regexp = "^[1-9]\\d*$", message = "任务主键ID必须为正整数")
                                         @RequestParam String pipelineid,
                                         @ApiParam(value = "数据来源库ID", required = true, example = "1")
                                         @Pattern(regexp = "^[1-9]\\d*$", message = "数据来源库ID必须为正整数")
                                         @RequestParam String databaseidSource,
                                         @ApiParam(value = "数据来源表名(区分大小写)", required = true, example = "table1")
                                         @NotBlank(message = "数据来源表名不能为空")
                                         @RequestParam String tablenameSource){

    FlinkDataField dataField = FlinkDataField.builder().build()
            .setPipelineid(Long.parseLong(pipelineid))
            .setDatabaseidSource(Long.parseLong(databaseidSource))
            .setTablenameSource(tablenameSource)
            ;

    return fieldService.getFlinkDataField(dataField);
}

 

@ApiModel()

用于描述一个数据模型的信息,即我们常用的实体、VO类、DTO类等描述

  在接口入参用实体类接收时,通常用该注解对实体类进行说明,一般与@ApiModelProperty结合使用,与上面两个参数注解达到的效果是一样的。

说明:该注解里面的汉字不能重复!!!每一个实体类对应的汉字说明必须具有唯一性!!!

否则,knife4j展示的接口文档所需的请求参数或响应参数与实际不符!!!

  @ApiModelProperty()

用于描述数据模型的属性信息

  对应的是方法名的请求入参

  @ApiIgnore

自动生成接口说明时忽略,可用于类、方法、方法入参

  @ApiResponses

响应状态描述(在控制类下方,首个方法名上方)

  对应到文档上是: 

小结

6 2.0.6以后的版本

  使用该版本,必须使用@EnableSwagger2WebMvc这一个注解。

  该配置类里的两个方法

  就可以合并成一个方法啦

        @Bean (value =         "defaultApi2" )
public Docket defaultApi2() {
    Docket docket =         new Docket(DocumentationType.SWAGGER_2)         // 选择swagger2版本
            // 接口文档的基本信息
            .apiInfo(        new ApiInfoBuilder()
                    .title(        "接口项目 后台服务API接口文档" )
                    .description(        "服务相关接口(knife4j)" )
                    // 服务Url(网站地址)
                    .termsOfServiceUrl(        "http://127.0.0.1:8080/" )
                    .contact(        new Contact(        "Marydon" ,        null ,        "marydon20170307@163.com" ))
                    .version(        "1.0" )
                    .build())
            //分组名称,不配置的话,默认值为:default
            .groupName(        "2.X版本" )
            .select()
            // 这里指定Controller扫描包路径(项目路径也行)
            // 方式一:配置扫描:所有想要在swagger界面统一管理的接口,都必须在此包下
            // .apis(RequestHandlerSelectors.basePackage("com.baidu.zhidao.web.controller.entrance"))
            // 方式二:只有当方法上有@ApiOperation注解时,才能生成对应的接口文档
            .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.        class ))
            // 路径使用any风格(指定所有路径)
            .paths(PathSelectors.any())
            .build();
    return docket;
}

  PS:不合并也是可以哒。   

  说明:

  使用Knife4j2.0.6及以上的版本,Spring Boot的版本必须大于等于2.2.x 

  需要注意的是:

  当knife4j<=1.9.6时,它是knife4j的前身,叫做:swagger-bootstrap-ui,必须用注解@EnableSwaggerBootstrapUi+@EnableSwagger2;

  不仅支持swagger-bootstrap-ui,原始的swagger-ui还是可以使用的,访问地址:

  http://${host}:${port}/swagger-ui.html

  当2.0.0<=knife4j<=2.0.5时,必须用注解@EnableKnife4j+@EnableSwagger2;

  当2.0.6<=knife4j<=2.0.8时,必须使用注解@EnableSwagger2WebMvc。

  当knife4j>1.9.6时,已经不支持swagger-ui了。

7.文档设置访问权限控制

  一般情况下,我们在正式环境下是要屏蔽接口文档的,只有在非生产环境(比如:开发环境、测试环境)下才允许访问,如何实现?

  可在application.properties/application.yml文件中添加以下配置。

  禁用文档

  properties文件:knife4j.production=true

  yml文件如下:

  重启服务,此时,再次访问doc.html,将是这个样子:

  启用文档:将值改为false即可(表示的含义是:非生产环境)

  这样一来,在打包的时候,如果允许访问,就将其值改成false;否则,就改成true。

  这种方式不够智能,打包的时候,我们还需要记得改值,太麻烦了,还有更简单的一种。

  通过注解@Profile来实现

  可以配置多个访问环境,中间使用逗号隔开;

  也可以使用!,!prod,就表示只要profile的值不等于prod,就可以访问得到文档;

  @profile里的值对应的是主配置文件application.yml中spring.profiles.active的值,如下图所示:

  spring.profiles.active的值的值其实是很随意的,跟开发环境、生产环境、预演环境、生产环境等没有半毛钱关系,只不过是大家习惯于这样使用,方便区分罢了。

  按照这种方式,基本上就已经满足普通开发者的需求了,也相当于实现了动态配置。

  说明:

  如果采取这种方式,下面的值必须是false或者直接删掉该配置。

  然而这种方式对于我来说还是太傻了,随着公司产品的推广,一个地方会添加一个配置文件,每个地方又都有正式和测试之分;

  这样的话,每个地方测试的时候,我得改下配置启用;在正式上线的时候再禁用,太麻烦!怎么办,有没有一劳永逸的办法?

  通过在pom.xml中配置多个profile实现

  在properties标签添加自定义属性isProduction,名称随意,值是固定的:true/false

  在主配置文件application引入该标签的值即可

  说明:yml文件引入pom.xml的值使用@标签名@,properties文件引入pom.xml的值使用${标签名}

  这样,我在打包的时候,就不需要手动去修改production的值,而是通过切换环境通过maven插件,将对应的值塞到主配置文件中。

  说明:

  这种方式,适用于大量配置文件,且需要来回切换环境的需求;

  通过这种配置方式,就无需再配置@Profile注解了,否则就有点画蛇添足了。

8.文档通过密码访问

  通过上面,我们只能控制环境来间接控制文档是否可访问,只要你能访问该接口文档,那么别人也一样可以访问得到,还不够安全。

  如果有更高的安全需求,我们还可以为文档添加访问密码。

  这样,当我们再次访问该文档时,就需要输入用户名和密码才能访问啦。 

9.自定义Footer

  修改这个显示内容

  前提:knife4j>=2.0.8

  第一步:必须开启增强功能

  第二步:设置Footer

  第三步:修改knife4j的配置类,比如:我上面叫做Knife4jConfig.java

  注入对象:OpenApiExtensionResolver

        // 自定义footer需要用到
@Resource
private OpenApiExtensionResolver openApiExtensionResolver;

  Docket增加扩展方法

        new Docket(DocumentationType.SWAGGER_2)
                // 扩展属性(footer)
                .extensions(openApiExtensionResolver.buildSettingExtensions());

  效果展示:

10.分组管理

2023年9月26日09:06:55

这个分组是真正意义上的分组,不同于下面的分组排序功能。

在swagger当中,我们可以配置多个分组,对接口进行管理。

如果需要的话,操作步骤如下:

1.创建不同的Docket,对组进行管理(一个Docket相当于一个组);

2.设置groupName,添加组名;

3.设置api,指定该组管理的作用域。

11.分组排序(对多个Controller进行排序)

  2020-12-16

  文档上,每一项对应的是一个Controller,每一个controller相当于一个小组,有时候我们会想给它们排排顺序,怎么搞?

  要想实现这个个性化的需求,需要满足三个条件:

  第一,knife4j>=2.0.8;

  第二,使用注解@ApiSupport;

  说明:每个控制层的order值,必须大于0,且最好间隔10以上。 

  第三,开启增强功能。

  增强功能需要通过配置yml配置文件开启:

knife4j:
  enable: true

12.接口排序(对同一个Controller当中的接口排序)

  每个控制器可能会对外公开多个接口,多个接口如何自定义排序呢?

  使用注解@ApiOperationSupport;

  其它两个条件,同上,缺一不可。    

13.不拦截swagger请求

2022年1月20日15:55:15

当我们配置springMVC拦截器后,swagger的请求也会被误拦;

所以,需要我们将其访问路径排除拦截之外。

*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*,/v2/api-docs,/swagger-resources,/doc.html,/

 或

/swagger-ui/index.html,/doc.html,/swagger-resources/**,/webjars/**, /static/**

具体实现方式,见文末推荐。

14.全局参数配置

2023年9月26日10:47:16

对于接口的访问,我们一般都有用户权限控制。

这该怎么调试接口呢?

比方说,我们可以设置全局参数,以token为例。

说明:配置好全局参数后,需要刷新页面,才会生效!

这样在调接口的时候,会自动添加到接口的请求头当中。

请求头部内容展示

2023年9月27日19:16:34

15.遇到一个奇葩问题

通常情况下,我们是遇不到的,但是,一旦遇到就会很抓狂。

table对象里面嵌套了columns数组。

正确的数据结构,应该是:

然鹅,table对象当中,却包含了其它对象的属性。

另一个实体类,长这样。

无论怎样尝试,都搞不定。

最终,同事发现,这两个对像的@ApiModel的备注一模一样。

这样就让swagger误以为:它俩是一体的!!!

实际上,在前端调用的时候,我们后台解析这次入参的数据结构时,并不受影响。

只不过,是swagger展示的数据结构,让我误以为,提供的数据结构不对。

这也算是给自己长了一个教训。

2023年9月28日10:34:48

16.knife4j注解+请求入参校验 用法示例

form表单格式请求(application/x-www-form-urlencoded)

form表单请求或者get请求,需要使用@RequestParam来标注入参。

@ApiOperation(value = "获取来源表名的字段信息", notes = "获取来源表名的字段信息", httpMethod = "GET")
@GetMapping(value = "/getSourceTableFieldList")
public List<TableFieldVo> getSourceTableFieldList(@ApiParam(value = "数据库ID", required = true, example = "1")
                                                  @Pattern(regexp = "^[1-9]\\d*$", message = "数据库ID必须为正整数")
                                                  @RequestParam String sourceDatabaseId,
                                                  @ApiParam(value = "表名(区分大小写)", required = true, example = "table1")
                                                  @NotBlank(message = "表名不能为空")
                                                  @RequestParam String sourceTableName,
                                                  @ApiParam(value = "目标表状态(0:不存在表;1:已存在表)", required = true, example = "1")
                                                  @Min(value = 0, message = "targetTableStatus:[0,1]")
                                                  @Max(value = 1, message = "targetTableStatus:[0,1]")
                                                  @RequestParam String targetTableStatus){

    // 根据数据库ID和表名查询所有字段信息
    return pipelineService.getSourceTableFieldList(sourceDatabaseId, sourceTableName, targetTableStatus);
}

另外,如果是非必传项,被@RequestParam注解修饰时,需要声明required属性值为false。

@RequestParam(required = false)

因为该注解的默认值是:必传项。 

@ApiOperation(value = "分页查询管道列表", notes = "分页查询管道列表", httpMethod = "GET")
@GetMapping(value = "/getPipelineList")
public PageList<FlinkDataPipeline> getPipelineList(@ApiParam(value = "管道名称", required = false, example = "管道1")
                                                   @RequestParam(required = false) String pipelinename){

    FlinkDataPipelineVo pipelineVo = FlinkDataPipelineVo.builder().build()
            .setPipelinename(pipelinename)
            ;
    return pipelineService.getPageList(pipelineVo);
}

json格式请求(application/json)

@ApiOperation(value = "添加管道信息", notes = "添加管道信息", httpMethod = "POST")
@PostMapping("/add")
public Boolean addPipelineAndField(@RequestBody @Valid PipelineFieldVo pipelineFieldVo){
    return pipelineService.addPipelineAndField(pipelineFieldVo);
}

被@RequestBody注解修饰的,接收入参的实体类,如果想要参数校验生效的话,除了需要在实体类对应的属性上增加校验之外,

还要在该实体类上增加@Valid注解,只有这样,对请求入参的校验才会生效。

说明:

要想使用校验规则生效,除了需要引入hibernate-validator.jar包外,还需要在控制器类名上增加@Validated注解。

更详细的用法,见文末推荐。

写在最后

  哪位大佬如若发现文章存在纰漏之处或需要补充更多内容,欢迎留言!!!

 相关推荐:

posted @ 2020-12-04 17:58  Marydon  阅读(10550)  评论(0编辑  收藏  举报