SpringBoot 3.x 结合 Swagger3 (Knife4j )踩坑实录

cnblogs 2024-06-22 14:39:00 阅读 93

SpringBoot 3.x + Swagger3 踩坑实录

我的是springboot 版本是:3.2.2

<parent>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>3.2.2</version>

<relativePath/> <!-- lookup parent from repository -->

</parent>

官方文档

官方文档(快速开始):

1,快速开始 | Knife4j (xiaominfo.com)

https://doc.xiaominfo.com/docs/quick-start

官方文档(详细配置):

2, 增强模式 | Knife4j (xiaominfo.com)

https://doc.xiaominfo.com/docs/features/enhance

3,版本参考

Knife4j版本参考 | Knife4j (xiaominfo.com)

如果自己springboot 是 2.X.X 的版本,可参考官方文档,进行不同的依赖配置

以下是一些常见的Spring Boot版本及其对应的Knife4j版本兼容推荐:

Knife4j在之前的版本更新中,逐渐提供了一些服务端适配的增强特性功能。

但是开发者应该明白,不管是Swagger2规范还是OpenAPI3规范,Knife4j的最新版本的纯Ui版本,是可以适配Spring Boot所有版本的。

如果你不考虑使用Knife4j提供的服务端增强功能,引入Knife4j的纯Ui版本没有任何限制。只需要考虑不同的规范即可

其实大部分的报错一般都是依赖问题(比如依赖缺少,版本冲突,版本不合)

错误操作

依赖

网上帖子一般说的结合 knife4j(Swagger3), 添加的依赖一般都只有knife4j。 但是这个是不对的,依赖没有完全,并且就算配置好yaml ,启动访问也会出错。完整依赖可看下面正确部分。

<dependency>

<groupId>com.github.xiaoymin</groupId>

<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>

<version>4.4.0</version>

</dependency>

yaml 配置

改的地方为:packages-to-scan: com.example.eip.controller(自己项目的controller 目录)

其他地方基本上不用改。

springdoc:

swagger-ui:

path: /swagger-ui.html

tags-sorter: alpha

operations-sorter: alpha

api-docs:

path: /v3/api-docs

group-configs:

- group: 'default'

paths-to-match: '/**'

#生成文档所需的扫包路径,一般为启动类目录

packages-to-scan: com.example.eip.controller

#knife4j配置

knife4j:

#是否启用增强设置

enable: true

#开启生产环境屏蔽

production: false

#是否启用登录认证

basic:

enable: true

username: admin

password: 123456

setting:

language: zh_cn

enable-version: true

enable-swagger-models: true

swagger-model-name: 用户模块

这个时候访问是会报错:http://localhost:8069/doc.html#/home

报错信息解决


报错信息一: void io.swagger.v3.oas.models.OpenAPI. (io.swagger.v3.oas.models.SpecVersion)

这个报错才是访问不到的根本原因

原因

jakarta.servlet.ServletException: Handler dispatch failed: java.lang.NoSuchMethodError: 'void io.swagger.v3.oas.models.OpenAPI.<init>(io.swagger.v3.oas.models.SpecVersion)'

at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1104)

at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)

at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)

报错信息二:No static resource favicon.ico 这个报错影响使用访问doc文档,但是不好看

原因:spring boot3项目中浏览器中访问报错找不到favicon.ico

目前springfox已经停止维护了。最近在升级底层框架时看到spring官方推荐使用springdoc

这个情况有人去Github提了issue,但是Spring开发老哥说了这个不是bug,那就只能自己解决了。

2024-05-17T16:41:52-dvhtzz.png

org.springframework.web.servlet.resource.NoResourceFoundException: No static resource favicon.ico.

at org.springframework.web.servlet.resource.ResourceHttpRequestHandler.handleRequest(ResourceHttpRequestHandler.java:585)

at org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter.handle(HttpRequestHandlerAdapter.java:52)

at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089)

at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)

at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)

at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903)

at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564)

at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)

at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)

at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205)

正确流程

这里先提供一下目录文件:

添加正确依赖

完整的添加所有的依赖,每个依赖都不能少,少就可能出错(可解决报错一)

<!-- 添加swagger核心依赖-->

<dependency>

<groupId>io.swagger.core.v3</groupId>

<artifactId>swagger-core</artifactId>

<version>2.2.20</version>

</dependency>

<!--添加knife4j依赖-->

<dependency>

<groupId>com.github.xiaoymin</groupId>

<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>

<version>4.4.0</version>

</dependency>

<!--添加Springdoc依赖-->

<dependency>

<groupId>org.springdoc</groupId>

<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>

<version>2.2.0</version>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-configuration-processor</artifactId>

<optional>true</optional>

</dependency>

<!--仅添加上述依赖,仍有可能报错,需补充以下依赖-->

<dependency>

<groupId>com.fasterxml.jackson.module</groupId>

<artifactId>jackson-module-jakarta-xmlbind-annotations</artifactId>

<version>2.13.3</version>

</dependency>

<dependency>

<groupId>javax.xml.bind</groupId>

<artifactId>jaxb-api</artifactId>

<version>2.4.0-b180830.0359</version>

</dependency>

添加yaml 配置

springdoc:

swagger-ui:

path: /swagger-ui.html

tags-sorter: alpha

operations-sorter: alpha

api-docs:

path: /v3/api-docs

group-configs:

- group: 'default'

paths-to-match: '/**'

#生成文档所需的扫包路径,一般为启动类目录

packages-to-scan: com.example.eip.controller

#knife4j配置

knife4j:

#是否启用增强设置

enable: true

#开启生产环境屏蔽

production: false

#是否启用登录认证

basic:

enable: true

username: admin

password: 123456

setting:

language: zh_cn

enable-version: true

enable-swagger-models: true

swagger-model-name: 用户模块

配置过滤静态资源

import jakarta.servlet.http.HttpServletRequest;

import jakarta.servlet.http.HttpServletResponse;

import org.springframework.boot.SpringBootConfiguration;

import org.springframework.http.HttpStatus;

import org.springframework.web.servlet.HandlerInterceptor;

import org.springframework.web.servlet.config.annotation.InterceptorRegistry;

import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**

* @Classname FaviconConfiguration

* @Description 添加配置文件,处理favicon.ico请求

* @Version 1.0.0

* @Date 2024/6/11 13:39

* @Created by Administrator

*/

@SpringBootConfiguration

public class FaviconConfiguration implements WebMvcConfigurer {

@Override

public void addInterceptors(InterceptorRegistry registry) {

registry.addInterceptor(new HandlerInterceptor() {

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {

if (!"GET".equalsIgnoreCase(request.getMethod()) || !request.getRequestURI().toString().equals("/favicon.ico")) {

return true;

}

response.setStatus(HttpStatus.NO_CONTENT.value()); // 设置状态码为204 No Content

return false;

}

}).addPathPatterns("/**");

}

}

这个时候就可以访问到文档:http://localhost:8069/doc.html#/home

增强模式

编写配置文件

这个部分注意是提供一些项目信息或者个人的信息

import io.swagger.v3.oas.models.OpenAPI;

import io.swagger.v3.oas.models.ExternalDocumentation;

import io.swagger.v3.oas.models.info.Contact;

import io.swagger.v3.oas.models.info.Info;

import io.swagger.v3.oas.models.info.License;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

/**

* @Classname SwaggerConfig

* @Description TODO

* @Version 1.0.0

* @Date 2024/6/11 14:16

* @Created by Administrator

*/

@Configuration

public class SwaggerConfig {

@Bean

public OpenAPI swaggerOpenAPI(){

return new OpenAPI()

.info(new Info().title("标题")

// 信息

.contact(new Contact().name("作者").email("邮箱").url("地址"))

// 简介

.description("我的API文档")

// 版本

.version("v1")

// 许可证

.license(new License().name("Apache 2.0").url("http://springdoc.org")))

.externalDocs(new ExternalDocumentation()

.description("外部文档")

.url("https://springshop.wiki.github.org/docs"));

}

}

配置后的效果:

配置启动链接接口地址

每次都要打开浏览器输入地址访问不友好,我们在启动类似进行地址配置

启动类上优化 或者 编写配置类

第一种方式: 编写配置类(推荐使用)

创建文件:DocumentationConfig

import io.micrometer.common.util.StringUtils;

import lombok.extern.slf4j.Slf4j;

import org.springframework.context.annotation.Configuration;

import org.springframework.core.env.Environment;

import java.net.InetAddress;

import java.net.UnknownHostException;

/**

* @Classname DocumentationConfig

* @Description TODO

* @Version 1.0.0

* @Date 2024/6/11 15:28

* @Created by Administrator

*/

@Configuration

@Slf4j

public class DocumentationConfig {

public void logApplicationStartup(Environment env) {

String protocol = "http";

if (env.getProperty("server.ssl.key-store") != null) {

protocol = "https";

}

String serverPort = env.getProperty("server.port");

String contextPath = env.getProperty("server.servlet.context-path");

if (StringUtils.isBlank(contextPath)) {

contextPath = "/doc.html";

} else {

contextPath = contextPath + "/doc.html";

}

String hostAddress = "localhost";

try {

hostAddress = InetAddress.getLocalHost().getHostAddress();

} catch (UnknownHostException e) {

log.warn("The host name could not be determined, using `localhost` as fallback");

}

log.info("""

----------------------------------------------------------

\t应用程序“{}”正在运行中......

\t接口文档访问 URL:

\t本地: \t\t{}://localhost:{}{}

\t外部: \t{}://{}:{}{}

\t配置文件: \t{}

----------------------------------------------------------""",

env.getProperty("spring.application.name"),

protocol,

serverPort,

contextPath,

protocol,

hostAddress,

serverPort,

contextPath,

env.getActiveProfiles());

}

}

第二种方式:启动类上优化

import io.micrometer.common.util.StringUtils;

import lombok.extern.slf4j.Slf4j;

import org.mybatis.spring.annotation.MapperScan;

import org.springframework.boot.Banner;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.core.env.Environment;

import java.net.InetAddress;

import java.net.UnknownHostException;

@SpringBootApplication

@MapperScan("com.mijiu.mapper")

@Slf4j

public class SpringbootTemplateApplication {

public static void main(String[] args) {

SpringApplication app = new SpringApplication(SpringbootTemplateApplication.class);

Environment env = app.run(args).getEnvironment();

app.setBannerMode(Banner.Mode.CONSOLE);

logApplicationStartup(env);

}

private static void logApplicationStartup(Environment env) {

String protocol = "http";

if (env.getProperty("server.ssl.key-store") != null) {

protocol = "https";

}

String serverPort = env.getProperty("server.port");

String contextPath = env.getProperty("server.servlet.context-path");

if (StringUtils.isBlank(contextPath)) {

contextPath = "/doc.html";

} else {

contextPath = contextPath + "/doc.html";

}

String hostAddress = "localhost";

try {

hostAddress = InetAddress.getLocalHost().getHostAddress();

} catch (UnknownHostException e) {

log.warn("The host name could not be determined, using `localhost` as fallback");

}

log.info("""

----------------------------------------------------------

\t应用程序“{}”正在运行中......

\t接口文档访问 URL:

\t本地: \t\t{}://localhost:{}{}

\t外部: \t{}://{}:{}{}

\t配置文件: \t{}

----------------------------------------------------------""",

env.getProperty("spring.application.name"),

protocol,

serverPort,

contextPath,

protocol,

hostAddress,

serverPort,

contextPath,

env.getActiveProfiles());

}

}

配置后的效果:点击可以直接访问

如果代码写的有问题,欢迎大家评论交流,进行指点!!!

也希望大家点个关注哦~~~~~~~~



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。