ETJava Beta | Java    注册   登录
  • 搜索:
  • SpringBoot 3.x 结合 Swagger3 (Knife4j )踩坑实录

    发表于      阅读(1)     博客类别:Crawler     转自:https://www.cnblogs.com/blbl-blog/p/18262203
    如有侵权 请联系我们删除  (页面底部联系我们)  

    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());
        }
     
    }
    

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

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

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