前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot项目集成knif4j,从此告别手写Api文档

SpringBoot项目集成knif4j,从此告别手写Api文档

作者头像
用户3587585
发布2021-09-29 15:30:50
1.1K0
发布2021-09-29 15:30:50
举报
文章被收录于专栏:阿福谈Web编程阿福谈Web编程

前言

作为一名后台开发人员,在前后端分离项目的开发过程中,我们写好了后台接口之后总免不了要给前端同事提供一份详细的API接口文档,写完一个接口又要补充一个接口的文档,过程还挺繁琐的。那么有没有一款工具让我们不用再些这些繁琐的API文档呢?答案是有的。之前我们在项目中配置swagger结合相关的注解来生成API文档界面,只是界面不是那么美观,还必须在每个接口控制器方法中添加很多的注解,代码侵入性比较强。

现在越来越多的开发人员使用Knif4j来生成API文档,它是升级版的swagger, 不仅具有美观的界面,而且不需要在控制器方法中添加非常多的参数注解。哪怕不加任何注解,只要在项目中集成并配置好Docket类bean,就能生成界面美观的API接口文档,而且还有接口调试功能。

1 项目介绍

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

一开始项目初衷是为了写一个增强版本的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开始,关于使用方法,请参考[版本说明](2.6 版本说明 | knife4j (xiaominfo.com))。

目前主要支持以Java开发为主,并且是依赖于大环境下使用的Spring MVCSpring BootSpring Cloud框架。

当然,Knife4j也提供了离线版本,只要是符合Swagger的OpenAPI版本的规范JSON,都可以通过简单的配置进行适配,离线版本是适合于任何语言中使用Swagger非常的灵活方便。

2 界面鉴赏

Knife4j采用Vue+And Design Vue组件重写,相关功能界面如下,供大家赏鉴:

接口文档显示界面如下:

接口调试界面如下:

Swagger Models功能

3 快速开始

本次示例使用Spring Boot作为脚手架来快速集成Knife4j,Spring Boot版本2.3.5.RELEASE,Knife4j版本2.0.7,完整代码可以去参考knife4j-spring-boot-fast-demo

第一步:在maven项目的pom.xml中引入Knife4j的依赖包,代码如下:

代码语言:javascript
复制
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>2.0.7</version>
</dependency>

第二步:新建Knife4j配置类,配置Docket bean:

代码语言:javascript
复制
@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.github.xiaoymin.knife4j.controller"))
                .paths(PathSelectors.any())
                .build();
        return docket;
    }
}

最終整个工程目录结构如下图:

IndexController.java包含一个简单的RESTful接口,代码示例如下:

代码语言:javascript
复制
@Api(tags = "首页模块")
@RestController
public class IndexController {
    @ApiImplicitParam(name = "name",value = "姓名",required = true)
    @ApiOperation(value = "向客人问好")
    @GetMapping("/sayHi")
    public ResponseEntity<String> sayHi(@RequestParam(value = "name")String name){
        return ResponseEntity.ok("Hi:"+name);
    }
}

此时,启动Spring Boot工程,在浏览器中访问:http://localhost:17790/doc.html

界面效果图如下:

5 blogserver项目集成knif4j

第一步:项目的pom.xml文件中引入knife4j-spring-boot-starter起步依赖

代码语言:javascript
复制
<dependency>
	<groupId>com.github.xiaoymin</groupId>
	<artifactId>knife4j-spring-boot-starter</artifactId>
	<version>3.0.2</version>
</dependency>

这里笔者用了最新的3.0.2版本

第二步:新建Knife4j配置类:

代码语言:javascript
复制
package org.sang.config;

import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import springfox.documentation.spi.service.contexts.SecurityContext;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;


@Configuration
@EnableSwagger2
@EnableKnife4j
@Import(BeanValidatorPluginsConfiguration.class)
public class Knife4jConfig {

    @Bean
    public Docket createRestApi(){

        Predicate<RequestHandler> selector1 = RequestHandlerSelectors
                .basePackage("org.sang.controller"); //扫描包改成自己项目下的Controller类所在的包 

        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("VBlog博客平台")
                .apiInfo(apiInfo())
                .select()
                .apis(selector1)
                .paths(PathSelectors.any())
                .build()
                .securityContexts(securityContexts())
                .securitySchemes(securitySchemas());

    }

    private ApiInfo apiInfo(){
        return new ApiInfoBuilder()
                .title("VBlog博客平台RESTful APIs")
                .description("VBlog博客平台 api接口文档")
                .version("1.0")
                .build();

    }

    private List<SecurityScheme> securitySchemas(){
        List<SecurityScheme> list = new ArrayList();
        list.add(new ApiKey("loginToken", "loginToken", "header"));
        return list;
    }


    private List<SecurityReference> securityReferences(){
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[]
                {new AuthorizationScope("global", "accessEverything")};
        List<SecurityReference> securityReferences = new ArrayList<>();
        securityReferences.add(new SecurityReference("loginToken", authorizationScopes));
        return securityReferences;
    }


    private List<SecurityContext> securityContexts(){

        List<SecurityContext> list = new ArrayList();
        SecurityContext securityContext = SecurityContext.builder()
                .securityReferences(securityReferences())
                .forPaths(PathSelectors.regex("/*"))
                .build();
        list.add(securityContext);

        return list;
    }
}

第三步:新建WebApplicationConfig类重写addViewControllers方法

代码语言:javascript
复制
package org.sang.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebApplicationConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
         registry.addViewController("/").setViewName("doc.html");
    }
}

重写addViewControllers方法的目的是为了服务启动后能顺利访问到API文档界面

第四步:在控制器类上加上@Api注解, 在路由方法上加上@ApiOperation注解对路由方法进行描述,加上@ApiImplicitParam对接口入参进行详细描述。

示例代码:

代码语言:javascript
复制
@RestController
@RequestMapping(path = "/role")
@Api(value="roleController", tags = "角色相关API")
public class RoleController {

    @Autowired
    private RoleService roleService;

    private static final Logger logger = LoggerFactory.getLogger(RoleController.class);

    @PostMapping(path = "/addRole")
    @ApiOperation(value = "addRole", notes = "添加角色", produces = "application/json",
            consumes = "application/json", response = RespBean.class)
    @ApiImplicitParam(name="role", value = "角色对象", dataTypeClass = Role.class, paramType="body", required = true)
    public RespBean<Integer> addRole(@RequestBody Role role) {
        logger.info("roleCode={},roleName={}",role.getRoleCode(),role.getRoleName());
        int addCount = roleService.addRole(role);
        RespBean<Integer> respBean = new RespBean<>(200, "success");
        respBean.setData(addCount);
        return respBean;
    }
}

@Api注解方法属性说明:

代码语言:javascript
复制
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Api {
    // Api名称
    String value() default "";
    // Api标签
    String[] tags() default {""};

    /** @deprecated */
    @Deprecated
    // Api描述,已过时
    String description() default "";

    /** @deprecated */
    @Deprecated
    // Api基础路径,已过时
    String basePath() default "";

    /** @deprecated */
    @Deprecated
    // Api位置,已过时
    int position() default 0;
    // 响应参数数据类型,json格式数据类型为application/json
    String produces() default "";
    // 请求参数数据类型
    String consumes() default "";
    // 协议:http|https|dubbo|rmi
    String protocols() default "";
    // 认证信息
    Authorization[] authorizations() default {@Authorization("")};
    // 是否隐藏
    boolean hidden() default false;
}

这个注解主要用在控制器类上

@ApiOperation注解方法属性说明

代码语言:javascript
复制
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiOperation {
    // Api操作方法名称
    String value();
    // Api操作方法注意点
    String notes() default "";
    // Api操作方法标签集合
    String[] tags() default {""};
    // 响应对象类
    Class<?> response() default Void.class;
    // 响应体容器
    String responseContainer() default "";
    // 响应体引用
    String responseReference() default "";
    // http请求类型
    String httpMethod() default "";

    /** @deprecated */
    @Deprecated
    int position() default 0;
    // Api操作方法昵称
    String nickname() default "";
    // 响应体参数类型,若无则与@Api中的该参数值保持一致
    String produces() default "";
    // 请求体参数类型,若无则与@Api中的该参数值保持一致
    String consumes() default "";
    // 接口请求协议,若无则与@Api中的该参数值保持一致
    String protocols() default "";
    // 接口调用认证信息,若无则与@Api中的该参数值保持一致
    Authorization[] authorizations() default {@Authorization("")};
    // 是否隐藏,默认显示
    boolean hidden() default false;
    // 响应头
    ResponseHeader[] responseHeaders() default {@ResponseHeader(
    name = "",
    response = Void.class
)};
    // 响应码,默认为200
    int code() default 200;
    // 扩展参数类别
    Extension[] extensions() default {@Extension(
    properties = {@ExtensionProperty(
    name = "",
    value = ""
)}
)};
    // 忽略json视图,默认否
    boolean ignoreJsonView() default false;
}

这个注解主要用在控制器类中的路由方法上

@ApiImplicitParams注解方法属性说明:

代码语言:javascript
复制
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiImplicitParams {
    ApiImplicitParam[] value();
}

这个注解主要用于控制器类中的路由方法中有多个参数时使用,它的value值是个@ApiImplicitParam数组

@ApiImplicitParam 注解方法属性说明:

代码语言:javascript
复制
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiImplicitParam {
    // 参数名称
    String name() default "";
    // 参数显示名称
    String value() default "";
    // 参数默认值
    String defaultValue() default "";
    // 允许的多个值
    String allowableValues() default "";
    // 是否必须
    boolean required() default false;
    
    String access() default "";
    // 是否允许多个
    boolean allowMultiple() default false;
    // 参数数据类型
    String dataType() default "";
    // 参数数据类型类
    Class<?> dataTypeClass() default Void.class;
    // 参数类型:query|body|path
    String paramType() default "";
    // 参数示例
    String example() default "";
    // 参数详细示例
    Example examples() default @Example({@ExampleProperty(
    mediaType = "",
    value = ""
)});
    
    String type() default "";
    // 参数格式
    String format() default "";
    // 是否允许参数值为空
    boolean allowEmptyValue() default false;
    // 参数是否只读
    boolean readOnly() default false;
    // 集合格式
    String collectionFormat() default "";
}

6 效果体验

第一步: 启动blogserver项目服务和vue-element-admin前端项目服务

启动blogserver项目服务时在IDEA中选中启动类BlogserverApplication类下的main函数->右键->Debug BlogserverApplication

启动vue-element-admin前端项目服务时在项目根目录下:右键->Git Bash Here

在弹出的命令控制台中输入命令npm run dev 然后回车

控制台出现如下日志信息代表启动成功

代码语言:javascript
复制
 App running at:
  - Local:   http://localhost:3000/
  - Network: unavailable

  Note that the development build is not optimized.
  To create a production build, run npm run build.

第二步:打开谷歌浏览器输入http://localhost:8081/blog/doc.html 后回车

由于后台项目中通过SpringSecurity配置了安全认证,浏览器首先会跳转到 http://localhost:3000/#/login 登录页面进项登录,登录成功后再次输入http://localhost:8081/blog/doc.html 后回车就进入了Api文档首页VBlog博客平台RESTful APIs 效果图如下:

点击文档下面的调试可进入接口调试界面

我们输入请求参数后,再点击右上角的发送按钮即可测试接口的可用性,下面的响应内容去可以看到接口的返回信息。这样我们就可以直接通过文档页面测试接口,而不需要打开postman来调试接口了。

更多关于Knif4j增强文档功能请读者查看官方文档增强模式部分:https://doc.xiaominfo.com/knife4j/documentation/enhance.html

7 小结

本文我们通过Knife4j的官方文档学习了Knife4j项目以及如何在自己的SpringBoot项目中集成knife4j-spring-boot-strater组件自动生成升级版的Swagger2API文档。总结起来就一下四个步骤:

  • pom.xml文件中引入knife4j-spring-boot-strater组件的起步依赖
  • 配置swagger2文档Docket类bean,在接口扫描基础包中制定自己项目中控制器类所在的包名
  • 重写WebMvcConfigurer#addViewControllers方法,添加文档文件doc.html视图
  • 控制器类上添加@Api注解, 控制器中操作方法上添加@ApiOperationApiImplicitParam 注解

其中,第四步为可选项,用户也可步添加这三个注解,knif4j也能根据Spring MVC的注解生成接口文档,只是在页面显示的Api接口很多值都是默认值。

好了,本文就写到这里,希望读者朋友们都能动手实践一遍,亲自体验一把knife4j带来的神奇功能!

参考文档

【1】Knife4j官方文档:

https://doc.xiaominfo.com/knife4j/documentation

【2】接口文档从Swagger升级成knife4j使用教程

https://baijiahao.baidu.com/s?id=1683466038755184828

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-09-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 阿福谈Web编程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 1 项目介绍
  • 2 界面鉴赏
  • 3 快速开始
  • 5 blogserver项目集成knif4j
  • 6 效果体验
  • 7 小结
  • 参考文档
相关产品与服务
Serverless HTTP 服务
Serverless HTTP 服务基于腾讯云 API 网关 和 Web Cloud Function(以下简称“Web Function”)建站云函数(云函数的一种类型)的产品能力,可以支持各种类型的 HTTP 服务开发,实现了 Serverless 与 Web 服务最优雅的结合。用户可以快速构建 Web 原生框架,把本地的 Express、Koa、Nextjs、Nuxtjs 等框架项目快速迁移到云端,同时也支持 Wordpress、Discuz Q 等现有应用模版一键快速创建。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档