前言
作为一名后台开发人员,在前后端分离项目的开发过程中,我们写好了后台接口之后总免不了要给前端同事提供一份详细的API
接口文档,写完一个接口又要补充一个接口的文档,过程还挺繁琐的。那么有没有一款工具让我们不用再些这些繁琐的API
文档呢?答案是有的。之前我们在项目中配置swagger
结合相关的注解来生成API
文档界面,只是界面不是那么美观,还必须在每个接口控制器方法中添加很多的注解,代码侵入性比较强。
现在越来越多的开发人员使用Knif4j
来生成API
文档,它是升级版的swagger
, 不仅具有美观的界面,而且不需要在控制器方法中添加非常多的参数注解。哪怕不加任何注解,只要在项目中集成并配置好Docket
类bean,就能生成界面美观的API
接口文档,而且还有接口调试功能。
1 项目介绍
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开始,关于使用方法,请参考[版本说明](2.6 版本说明 | knife4j (xiaominfo.com))。
目前主要支持以Java开发为主,并且是依赖于大环境下使用的Spring MVC
、Spring Boot
、Spring 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的依赖包,代码如下:
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>2.0.7</version>
</dependency>
第二步:新建Knife4j
配置类,配置Docket
bean:
@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接口,代码示例如下:
@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
代码语言:javascript复制第一步:项目的
pom.xml
文件中引入knife4j-spring-boot-starter
起步依赖
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.2</version>
</dependency>
这里笔者用了最新的3.0.2版本
代码语言:javascript复制第二步:新建
Knife4j
配置类:
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;
}
}
代码语言:javascript复制第三步:新建
WebApplicationConfig
类重写addViewControllers
方法
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
注解方法属性说明:
@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
注解方法属性说明
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
注解方法属性说明:
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiImplicitParams {
ApiImplicitParam[] value();
}
这个注解主要用于控制器类中的路由方法中有多个参数时使用,它的value值是个@ApiImplicitParam
数组
@ApiImplicitParam
注解方法属性说明:
@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
组件自动生成升级版的Swagger2
API文档。总结起来就一下四个步骤:
- pom.xml文件中引入
knife4j-spring-boot-strater
组件的起步依赖 - 配置
swagger2
文档Docket
类bean,在接口扫描基础包中制定自己项目中控制器类所在的包名 - 重写
WebMvcConfigurer#addViewControllers
方法,添加文档文件doc.html
视图 - 控制器类上添加
@Api注解
, 控制器中操作方法上添加@ApiOperation
和ApiImplicitParam
注解
其中,第四步为可选项,用户也可步添加这三个注解,knif4j
也能根据Spring MVC
的注解生成接口文档,只是在页面显示的Api
接口很多值都是默认值。
好了,本文就写到这里,希望读者朋友们都能动手实践一遍,亲自体验一把knife4j
带来的神奇功能!
参考文档
【1】Knife4j官方文档:
https://doc.xiaominfo.com/knife4j/documentation
【2】接口文档从Swagger升级成knife4j使用教程
https://baijiahao.baidu.com/s?id=1683466038755184828