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


    SpringBoot 3.x + Swagger3 踩坑实录

    我的是springboot 版本是:3.2.2

    <parent>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-parentartifactId>
            <version>3.2.2version>
            <relativePath/> 
        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.xiaoymingroupId>
                <artifactId>knife4j-openapi3-jakarta-spring-boot-starterartifactId>
                <version>4.4.0version>
            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.(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)
    

    正确流程

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

    添加正确依赖

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

    
            <dependency>
                <groupId>io.swagger.core.v3groupId>
                <artifactId>swagger-coreartifactId>
                <version>2.2.20version>
            dependency>
            
            <dependency>
                <groupId>com.github.xiaoymingroupId>
                <artifactId>knife4j-openapi3-jakarta-spring-boot-starterartifactId>
                <version>4.4.0version>
            dependency>
            
            <dependency>
                <groupId>org.springdocgroupId>
                <artifactId>springdoc-openapi-starter-webmvc-apiartifactId>
                <version>2.2.0version>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-configuration-processorartifactId>
                <optional>trueoptional>
            dependency>
            
            <dependency>
                <groupId>com.fasterxml.jackson.modulegroupId>
                <artifactId>jackson-module-jakarta-xmlbind-annotationsartifactId>
                <version>2.13.3version>
            dependency>
            <dependency>
            <groupId>javax.xml.bindgroupId>
            <artifactId>jaxb-apiartifactId>
            <version>2.4.0-b180830.0359version>
            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());
        }
     
    }
    

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

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

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

  • 相关阅读:
    Mybatis-Plus条件构造器学习and方法
    TCP/IP 详解(第 2 版) 笔记 / 3 链路层 / 3.5 无线局域网 - IEEE 802.11 (Wi-Fi) / 3.5.5 Wi-Fi 安全
    nodejs+vue+elementui社区居民信息管理及数据分析与可视化系统设计
    Yolov安全帽佩戴检测 危险区域进入检测 - 深度学习 opencv 计算机竞赛
    不降逼格的情况下降价格!Chromebook Plus让创新延续和计算机重生
    UE4动作游戏实例RPG Action解析二:GAS系统播放武器绑定的技能,以及GE效果
    第二章 数据结构(2)
    自学100天,成功转行软件测试,浅谈一下我的转行经历,并分享一下我的学习经验~
    Emiya 家今天的饭(计数4, dp19)
    通信中间件 Fast DDS 基础概念简述与通信示例
  • 原文地址:https://www.cnblogs.com/blbl-blog/p/18262203