现在的项目开发,绝大多数都已经采用前后端分离,前后端开发人员必须依靠接口文档进行协作。当前最流行的文档生成工具就是 Swagger,它是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。但是本篇博客介绍的是 Knife4j ,它是集 Swagger 和 OpenAPI 为一体的增强解决方案,拥有更多更强大的功能。本篇博客通过做 Demo 演示如何使用 Knife4j ,在本篇博客的最后会提供源代码下载。
Swagger 官网地址:https://swagger.io
Knife4j 官网地址:https://doc.xiaominfo.com
一、搭建工程
搭建一个 SpringBoot 工程,具体结构如下:
如果想使用 Knife4j ,首先需要在 pom 文件中引入 knife4j-spring-boot-starter 依赖包。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
</parent>
<groupId>com.jobs</groupId>
<artifactId>springboot_knife4j</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
<!--使用 knife4j 功能,只引入这一个依赖包即可-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.5</version>
</plugin>
</plugins>
</build>
</project>
下面列出 application.yml 配置文件的内容:
server:
port: 8888
knife4j:
# 是否启用增强版功能
enable: true
# 如果是生产环境,将此设置为 true,然后就能够禁用了 knife4j 的页面
production: false
Knife4j 的一个非常不错的功能就是可以通过 knife4j.production 配置是否是生产环境,如果配置为 true 的话,那么就不会展示出接口文档的页面,确保生产环境的安全性。当然有关 knife4j 还有很多其它的实用配置项,详情可以参考官网。
Knife4j 在运行过程中,会自动生成一个 doc.html 静态页面,这个就是我们要访问的接口文档页面。默认情况下 SpringBoot 是不允许访问静态资源的,因此我们需要在 SpringBoot 中配置 Knife4j 的静态资源请求映射路径。
另外需要使用 @EnableOpenAPI 注解。由于我们的接口都是在 controller 类中进行编写,因此需要配置 Knife4j 需要扫描的 controller 包。本博客的具体代码细节都编写在了 WebMvcConfig 类中了,具体细节如下:
package com.jobs.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
//需要添加这个注解 @EnableOpenApi
@EnableOpenApi
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
//需要配置 knife4j 的静态资源请求映射地址
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/doc.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
@Bean
public Docket createDocket() {
// 文档类型
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.jobs.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("我的测试")
.version("1.0")
.description("我的测试接口文档")
.build();
}
}
在实际的项目中,绝大多数情况下,网站必须登录后才能查看页面,为了能够匿名查看 knife4j 的接口文档页面,我们必须放行 Knife4j 的静态资源文件。本篇博客采用自己编写的 filter 来验证用户登录,因此需要在 filter 中放行 Knife4j 的静态资源文件。
package com.jobs.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jobs.entity.Result;
import org.springframework.util.AntPathMatcher;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter(filterName = "LoginCheckFilter", urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String requestURI = request.getRequestURI();
if (checkPassUri(requestURI)) {
filterChain.doFilter(request, response);
return;
}
Object uid = request.getSession().getAttribute("user");
if (uid != null) {
filterChain.doFilter(request, response);
return;
}
response.getWriter().write(new ObjectMapper().writeValueAsString(Result.fail(-99,"no login")));
}
//路径匹配对象
private static final AntPathMatcher apm = new AntPathMatcher();
private boolean checkPassUri(String requestURI) {
String[] uris = new String[]{
//放行用户登录接口
"/user/login",
//放行用户退出接口
"/user/logout",
//放行下面的 knifefj 的静态资源文件路径
"/doc.html",
"/webjars/**",
"/swagger-resources",
"/v2/api-docs"
};
for (String uri : uris) {
boolean match = apm.match(uri, requestURI);
if (match) {
return true;
}
}
return false;
}
}
为了能够是 filter 生效,需要在 SpringBoot 启动类上增加 @ServletComponentScan 注解。
package com.jobs;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@Slf4j
@ServletComponentScan
@SpringBootApplication
public class MainApp {
public static void main(String[] args) {
SpringApplication.run(MainApp.class, args);
log.info("项目启动成功");
}
}
OK,项目结构已经搭建完毕了,下面就看具体的代码吧。
二、代码细节查看
其实现在启动 SpringBoot 程序,访问 localhost:8888/doc.html 就已经可以看到接口文档了。但是文档中没有任何注释,为了能够让接口文档更容易看懂,需要在代码中增加以下相关的注解。
注解 | 位置 | 说明 |
---|---|---|
@Api | 类(Controller) | 加载 Controller类上表示对类的说明 |
@ApiModel | 类(实体类) | 描述实体类的作用 |
@ApiModelProperty | 属性(实体类) | 描述实体类的属性 |
@ApiOperation | 方法(接口) | 说明方法的用途、作用 |
@ApiImplicitParams | 方法(接口参数) | 表示一组参数说明 |
@ApiImplicitParam | 方法(接口参数) | 用在 @ApiImplicitParams 注解中,指定一个请求参数的各个方面的属性 |
下面就让我们把上面的注解,添加到具体的代码中,详情可下载源代码查看。
1 在实体类上使用 @ApiModel 和 @ApiModelProperty 注解
package com.jobs.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
@Data
@ApiModel("用户")
public class User implements Serializable {
@ApiModelProperty("用户id")
private Long id;
@ApiModelProperty("用户名称")
private String name;
@ApiModelProperty("用户年龄")
private Integer age;
}
package com.jobs.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
@ApiModel("订单")
public class Order implements Serializable {
@ApiModelProperty("订单id")
private Long id;
@ApiModelProperty("订单名称")
private String name;
@ApiModelProperty("订单价格")
private BigDecimal price;
@ApiModelProperty("订单数量")
private Integer num;
}
2 在 Controller 上使用 @Api 注解,在接口上使用 @ApiOperation、@ApiImplicitParams 和 @ApiImplicitParam 注解
package com.jobs.controller;
import com.jobs.entity.Result;
import com.jobs.entity.User;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@Api(tags = "用户操作相关接口")
@RequestMapping("/user")
@RestController
public class UserController {
@ApiOperation("用户登录")
@ApiImplicitParams({
@ApiImplicitParam(name = "name", value = "用户名", required = true),
@ApiImplicitParam(name = "pwd", value = "密码", required = true)
})
@PostMapping("/login")
public Result<String> login(String name, String pwd, HttpServletRequest request) {
if ("jobs".equals(name) && "123".equals(pwd)) {
request.getSession().setAttribute("user", "jobs");
return Result.success("登录成功");
} else {
return Result.fail(-1, "用户名或密码不正确");
}
}
@ApiOperation("用户退出")
@PostMapping("/logout")
public Result<String> logout(HttpServletRequest request) {
request.getSession().removeAttribute("user");
return Result.success("退出成功");
}
@ApiOperation("添加用户")
@PostMapping
public Result<User> Add(@RequestBody User user) {
return Result.success(user);
}
}
package com.jobs.controller;
import com.jobs.entity.Order;
import com.jobs.entity.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Random;
@Api(tags = "订单操作相关接口")
@RequestMapping("/order")
@RestController
public class OrderController {
@ApiOperation("添加订单")
@PostMapping
public Result<Order> add(@RequestBody Order order) {
return Result.success(order);
}
@ApiOperation("根据id获取订单")
@ApiImplicitParam(name = "id", value = "订单id", required = true)
@GetMapping("/{id}")
public Result<Order> get(@PathVariable("id") Long id) {
Order order = new Order();
order.setId(id);
order.setName("订单" + new Random().nextInt());
order.setPrice(new BigDecimal(new Random().nextInt(200))
.divide(new BigDecimal("3"), 2, RoundingMode.HALF_UP));
order.setNum(new Random().nextInt(100));
return Result.success(order);
}
@ApiOperation("修改订单")
@PutMapping
public Result<String> update(@RequestBody Order order) {
return Result.success("修改成功");
}
@ApiOperation("删除订单")
@ApiImplicitParam(name = "id", value = "订单id", required = true)
@DeleteMapping
public Result<String> delete(Long id) {
return Result.success("删除成功");
}
}
OK,主要的代码已经添加好注解了,下面就可以启动 SpringBoot 程序,验证一下效果了。
三、验证效果
启动 SpringBoot 程序,访问 localhost:8888/doc.html 就可以看到文档接口页面了。
点击具体一个接口,可以查看接口详细信息:
可以进行接口的调用测试:
knife4j 还提供了导出离线文档的功能,如导出为 html 、markdown 以及 word 文档:
本篇博客的源代码下载地址为:https://files.cnblogs.com/files/blogs/699532/springboot_knife4j.zip