告别手写接口文档,Springboot集成Knife4j

442 阅读3分钟

前言

knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案,前身是swagger-bootstrap-ui,取名kni4j是希望她能像一把匕首一样小巧,轻量,并且功能强悍!

knife4j的前身是swagger-bootstrap-ui,为了契合微服务的架构发展,由于原来swagger-bootstrap-ui采用的是后端Java代码+前端Ui混合打包的方式,在微服务架构下显的很臃肿,因此项目正式更名为knife4j

怎样使用

添加依赖

既然是用 Maven 来管理项目依赖,那我们在项目 pom.xml 中引入 Knife4j 的相关依赖包,引入代码如下。

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>3.0.3</version>
</dependency>

配置添加 接着在项目中创建一个配置包 config,用于配置 Swagger 的配置依赖。在这里可以配置扫描的 controller 所在的包,设置接口文档的标题、描述、作者信息等。

这里其实和 Swagger2 和 Swagger3 很相似,Swagger 也是可以通过配置类来指定这些信息。

package com.example.knife4.config;

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.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class Knife4jConfiguration {
    @Bean(value = "defaultApi2")
    public Docket defaultApi2() {
        String groupName="3.X版本";
        Docket docket=new Docket(DocumentationType.OAS_30)
                .apiInfo(new ApiInfoBuilder()
                        .title("测试knife4j API ")
                        .description("# 这里记录服务端所有的接口的入参,出参等等信息")
                        .termsOfServiceUrl("http://yaomaoyang.com")
                        .contact(new Contact("空城泪","http://yaomaoyang.com","admin@qq.com"))
                        .version("3.0")
                        .build())
                //分组名称
                .groupName(groupName)
                .select()
                //这里指定Controller扫描包路径
                .apis(RequestHandlerSelectors.basePackage("com.example.knife4.controller"))
                .paths(PathSelectors.any())
                .build();
        return docket;
    }
}

编写 Controller 层

接着我们编写一个测试的 controller,用于在 Knife4j 中展示用,代码如下。

package com.example.knife4.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Api(tags = "测试swagger")
public class HelloController {

    @ApiImplicitParam(name = "name", value = "姓名", required = true)
    @GetMapping("/test")
    @ApiOperation("测试接口")
    public String test(@RequestParam(value = "name") String name){
        return "helloWorld"+":"+name;
    }
}

启动测试(注意)

这里需要注意的是程序此时会启动报错

2022-12-26 12:11:19.879 ERROR 96557 --- [nio-70-Acceptor] org.apache.tomcat.util.net.Acceptor      : Socket accept failed

java.nio.channels.AsynchronousCloseException: null
	at java.nio.channels.spi.AbstractInterruptibleChannel.end(AbstractInterruptibleChannel.java:205) ~[na:1.8.0_345]
	at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:256) ~[na:1.8.0_345]
	at org.apache.tomcat.util.net.NioEndpoint.serverSocketAccept(NioEndpoint.java:547) ~[tomcat-embed-core-9.0.70.jar:9.0.70]
	at org.apache.tomcat.util.net.NioEndpoint.serverSocketAccept(NioEndpoint.java:79) ~[tomcat-embed-core-9.0.70.jar:9.0.70]
	at org.apache.tomcat.util.net.Acceptor.run(Acceptor.java:129) ~[tomcat-embed-core-9.0.70.jar:9.0.70]
	at java.lang.Thread.run(Thread.java:750) [na:1.8.0_345]

2022-12-26 12:11:19.892  INFO 96557 --- [           main] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-12-26 12:11:19.952 ERROR 96557 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
	at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181) ~[spring-context-5.3.24.jar:5.3.24]
	at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54) ~[spring-context-5.3.24.jar:5.3.24]
	at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356) ~[spring-context-5.3.24.jar:5.3.24]
	at java.lang.Iterable.forEach(Iterable.java:75) ~[na:1.8.0_345]
	at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155) ~[spring-context-5.3.24.jar:5.3.24]
	at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123) ~[spring-context-5.3.24.jar:5.3.24]
	at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:935) ~[spring-context-5.3.24.jar:5.3.24]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586) ~[spring-context-5.3.24.jar:5.3.24]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.7.jar:2.7.7]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) [spring-boot-2.7.7.jar:2.7.7]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) [spring-boot-2.7.7.jar:2.7.7]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) [spring-boot-2.7.7.jar:2.7.7]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) [spring-boot-2.7.7.jar:2.7.7]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) [spring-boot-2.7.7.jar:2.7.7]
	at com.example.knife4.Knife4Application.main(Knife4Application.java:10) [classes/:na]

1、解决此问题需要在外面的配置文件 .yml文件中添加如下配置

spring:
   mvc:
     pathmatch:
        # Springfox使用的路径匹配是基于AntPathMatcher的,而Spring Boot 2.6.X使用的是PathPatternMatcher
        # 所以需要配置此参数
        matching-strategy: ant_path_matcher

2、或者我们在启动类上加上 @EnableWebMvc注解 注意:加上此注解之后打开http://localhost:8080/doc.html 会出现页面404错误,ideal中出现No mapping for GET /doc.html 这是因为我们为了解决上面的 Bug 而使用到了 @EnableWebMvc,由于它实现了 WebMvcConfigurer 接口,所以会导致我们访问识别。

处理方式: 在项目的 config 包下,我们新建一个配置类 WebMvcConfigurer 记成 WebMvcConfigurationSupport 类,接着将 dom.html 过滤掉即可。

@Configuration
public class WebMvcConfigurer extends WebMvcConfigurationSupport {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
        registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
        super.addResourceHandlers(registry);
    }
}

项目启动,网页打开 http://localhost:8080/doc.html ,就会出现以下的主界面。这里有我们之前在配置类中所设置的一些接口信息,此外,还对接口进行了统计。因为我们代码中只写了一个 GET 的请求,所以这里统计出只有一个 GET 请求。

image.png

打开具体接口,这里就有我们接口的请求和响应的一些情况说明。

image.png