返回

修复 Spring Boot 3.2 和 springdoc 2.2 的 Swagger UI 无法加载问题

java

解决 Spring Boot 3.2.10 与 springdoc-openapi 2.2.0 下 Swagger UI 无法加载 (No static resource swagger-ui/index.html)

不少哥们儿升级到 Spring Boot 3.2.10 和 springdoc-openapi-starter-webflux-ui 2.2.0 之后,发现 Swagger UI 刷不出来了,浏览器控制台或者后台日志里杵着个明晃晃的错误:org.springframework.web.servlet.resource.NoResourceFoundException: No static resource swagger-ui/index.html.。这感觉就像准备开车出门,结果发现车轱辘不见了。别急,这事儿能解决。

你的 pom.xml 依赖大概是这样:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.10</version>
    <relativePath />
</parent>
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
    <version>2.2.0</version>
    <exclusions>
        <exclusion>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Swagger 配置可能长这样:

@Configuration
public class SwaggerConfig {

    @Value("${Host:}")
    private String configUrl;

    @Bean
    public OpenAPI OpenApi() {
        Contact contact = new Contact();
        contact.setName("Team ");
        contact.setEmail("email.com");
        OpenAPI openAPI = new OpenAPI();
        if (StringUtils.isNotEmpty(configUrl)) {
            openAPI.addServersItem(
                    new Server().url(configUrl));
        }
        openAPI.info(new Info().title(" Scheduler API")
                .description("To schedulejobs").version("0.0.1-SNAPSHOT")
                .contact(contact)
        );
        return openAPI;
    }
}

遇到这问题,心里大概率会嘀咕:“版本都挺新的,怎么就不行了呢?”

二、抽丝剥茧:问题成因分析

No static resource swagger-ui/index.html 这个错误直截了当,意思就是 Spring Boot 找不到 swagger-ui/index.html 这个静态文件。Swagger UI 的界面是由一堆 HTML, CSS, JavaScript 静态文件组成的。springdoc-openapi-starter-webflux-ui 这个依赖包,会把这些静态文件打进你的应用,并通过 Spring WebFlux 的静态资源处理器机制暴露出来。

问题可能出在以下几个方面:

  1. 静态资源路径配置问题springdoc-openapi 默认会将 Swagger UI 的资源文件映射到 /webjars/swagger-ui/ 目录下,并提供一个方便访问的路径,通常是 /swagger-ui.html (它会重定向到 index.html) 或者直接访问 /swagger-ui/index.html。如果这个映射关系因为某些配置出了岔子,那自然就找不到了。
  2. Spring Security 拦截 :如果你的项目里用了 Spring Security,它老人家可是个尽职的门卫。默认情况下,它可能会把访问 /swagger-ui/** 或相关API文档路径(如 /v3/api-docs/** )的请求给拦下来,因为它觉得这些请求没“票”(未认证)。
  3. WebFlux 静态资源处理器配置冲突或缺失 :Spring Boot WebFlux 对静态资源有一套自动配置。但如果你自定义了 WebFluxConfigurer 并重写了 addResourceHandlers 方法,又不小心覆盖或未正确配置 Swagger UI 的资源路径,就可能导致这个问题。
  4. 项目上下文路径 (Context Path) 设置 :如果你的应用设置了 server.servlet.context-path (对传统 Servlet 应用) 或 spring.webflux.base-path (对 WebFlux 应用),那么访问 Swagger UI 的基础 URL 会发生变化。虽然报错信息是内部找不到资源,但也可能是因为组合出来的最终访问路径不正确,或者内部解析时也受到了这个 base-path 的影响。
  5. 依赖冲突或版本不兼容 :虽然 Spring Boot 3.2.10 和 springdoc-openapi 2.2.0 通常是好搭档,但复杂的项目依赖关系偶尔也会导致意想不到的火花。比如,你可能无意中引入了不同版本的 Webjars 或者 Servlet API 相关的依赖,造成了冲突。

那个 snakeyaml 的排除,通常是为了解决特定版本 snakeyaml 的安全漏洞,或者与 Spring Boot 管理的 snakeyaml 版本冲突。一般来说,这个排除本身不太可能直接导致静态资源找不到,除非 springdoc-openapi 的某个深层逻辑在特定情况下因为缺少了某个期望的 snakeyaml 版本而行为异常 (概率较低)。

三、对症下药:解决方案逐个击破

知道了可能的原因,我们就可以挨个试试看。

方案一:确认 Spring Doc UI 路径配置

springdoc-openapi 提供了一些配置属性来控制 Swagger UI 的行为,其中最关键的是 UI 路径。

  • 原理和作用
    springdoc.swagger-ui.path 属性可以自定义 Swagger UI 页面的访问路径。springdoc-openapi-starter-webflux-ui 默认会把 Swagger UI 页面注册在 /swagger-ui.html,它会重定向到实际的 index.html。所以,/swagger-ui/index.html 是核心的资源文件路径。

  • 操作步骤
    在你的 application.propertiesapplication.yml 文件中检查或显式设置这个路径。

    对于 application.properties:

    # 默认情况下,这个配置可以不写,springdoc会自动配置
    # 如果你发现访问 /swagger-ui.html 没用,或者想换个路径,可以试试明确指定
    # springdoc.swagger-ui.path=/swagger-ui.html
    
    # 或者你想让API文档路径也统一管理 (可选)
    # springdoc.api-docs.path=/v3/api-docs
    

    如果你在用 application.yml:

    springdoc:
      swagger-ui:
    #    path: /swagger-ui.html # 同样, 默认可不写
      api-docs:
    #    path: /v3/api-docs
    

    一般情况,这个属性保持默认就好。出问题时,可以尝试访问 /swagger-ui.html 看看是否重定向。如果连 /swagger-ui.html 都404,那问题可能更深层。

  • 额外建议
    确保你访问的是正确的路径。如果应用有 context-path (比如 /myapp),那么 Swagger UI 的完整路径应该是 http://localhost:8080/myapp/swagger-ui.html

方案二:配置 Spring Security 放行 Swagger 相关路径

这是最常见的原因之一,特别是当项目集成了 Spring Security 时。

  • 原理和作用
    Spring Security 会保护应用的所有端点。你需要明确告诉它,哪些路径是不需要身份验证就能访问的,比如 Swagger UI 界面本身和它获取 API 定义的路径 (/v3/api-docs 等)。

  • 代码示例 (使用 SecurityFilterChain)
    Spring Boot 3.x 推荐使用基于组件的 SecurityFilterChain bean 配置。

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
    import org.springframework.security.config.web.server.ServerHttpSecurity;
    import org.springframework.security.web.server.SecurityWebFilterChain;
    
    @Configuration
    @EnableWebFluxSecurity // 如果你用了响应式安全
    public class SecurityConfig {
    
        // Swagger UI 静态资源路径
        private static final String[] SWAGGER_UI_RESOURCES = {
                "/swagger-ui.html",
                "/swagger-ui/**",
                "/v3/api-docs/**", // OpenAPI 规范 JSON/YAML 文件的路径
                "/webjars/swagger-ui/**" // Swagger UI 自身的静态资源
        };
    
        @Bean
        public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
            http
                .authorizeExchange(exchanges -> exchanges
                    .pathMatchers(SWAGGER_UI_RESOURCES).permitAll() // 放行 Swagger UI 相关路径
                    .pathMatchers("/public/**").permitAll() // 举例:其他公开路径
                    .anyExchange().authenticated() // 其他所有请求都需要认证
                )
                .httpBasic(ServerHttpSecurity.HttpBasicSpec::disable) // 如果不用 HTTP Basic
                .csrf(ServerHttpSecurity.CsrfSpec::disable); // WebFlux下CSRF通常也需要针对性处理,此处为简化
    
            return http.build();
        }
    }
    

    注意 : @EnableWebFluxSecurity 是用于 WebFlux 应用的。如果你错误地在一个 MVC 项目里用了 springdoc-openapi-starter-webflux-ui 并配置了 WebFlux Security, 这本身就是个问题。确保你的项目类型和依赖选择一致。如果你是MVC项目,应该用 springdoc-openapi-starter-webmvc-ui@EnableWebSecurity

  • 安全建议

    • 生产环境中,不建议完全公开 Swagger UI。最好是将其置于某种形式的认证保护之下,或者仅在开发 (dev) 和测试 (test) 环境中启用。
    • 可以通过 Spring Profiles (@Profile("dev")) 来只在特定环境加载上述安全配置或 Swagger 本身的配置。
  • 进阶使用技巧
    对于更细致的控制,你可以将 SWAGGER_UI_RESOURCES 数组中的路径配置在 application.properties/yml 中,然后在 SecurityConfig 中读取这些配置,这样修改路径时就不用改 Java 代码了。

方案三:检查 WebFlux 静态资源处理器配置

如果你自定义了 WebFluxConfigurer,可能会影响默认的静态资源处理。

  • 原理和作用
    Spring Boot WebFlux 会自动配置静态资源服务。它会从一些预定义的 classpath 位置(如 classpath:/static/, classpath:/public/, classpath:/resources/, classpath:/META-INF/resources/)以及 Webjars (如 classpath:/META-INF/resources/webjars/) 加载静态内容。springdoc-openapi-ui 包将 Swagger UI 静态文件打包为 Webjars 资源。如果你的自定义配置没有正确地包含或代理 Webjars 的处理器,或者覆盖了默认的路径匹配规则,Swagger UI 的静态文件就可能访问不到了。

  • 操作步骤/代码示例
    如果你确实需要自定义 WebFluxConfigurer,确保不要破坏 Webjars 的处理。大多数情况下,你根本不需要自定义 addResourceHandlers。如果一定要,可以参考如下(但这只是确保基础的静态资源和Webjars工作,通常Spring Boot自动配置做得更好):

    // import org.springframework.context.annotation.Configuration;
    // import org.springframework.web.reactive.config.ResourceHandlerRegistry;
    // import org.springframework.web.reactive.config.WebFluxConfigurer;
    
    // @Configuration
    // public class CustomWebFluxConfig implements WebFluxConfigurer {
    
    //     @Override
    //     public void addResourceHandlers(ResourceHandlerRegistry registry) {
    //         // 这是Spring Boot通常会做的一部分,确保webjars可以访问
    //         registry.addResourceHandler("/webjars/**")
    //                 .addResourceLocations("classpath:/META-INF/resources/webjars/");
    
    //         // 如果你还有其他自定义的静态资源目录
    //         registry.addResourceHandler("/static/**")
    //                 .addResourceLocations("classpath:/static/");
    
    //         // 非常重要:除非你知道自己在做什么,否则不要随便覆盖或完全自定义 addResourceHandlers
    //         // Spring Boot 的自动配置通常已经足够好,并且能正确处理 springdoc-openapi 的 UI 资源。
    //         // 如果添加了 @EnableWebFlux 注解,则必须自己配置所有东西,包括这里。
    //     }
    // }
    

    强烈建议 :移除不必要的 WebFluxConfigurer 实现,尤其是那种仅仅是为了一个简单配置而全盘接管 addResourceHandlers 的情况。同时,检查是否在配置类上使用了 @EnableWebFlux 注解。这个注解会完全禁用 Spring Boot 对 WebFlux 的自动配置。如果你用了它,那你就得自己负责配置静态资源、消息转换器等所有东西。

  • 额外建议
    如果移除了 @EnableWebFlux 注解(如果你之前有的话)或自定义的 WebFluxConfigurer,很多因自动配置被禁用而产生的问题(包括静态资源问题)往往能迎刃而解。

方案四:核查应用上下文路径

如果你设置了应用的根路径 (context path 或 base path),访问 URL 自然需要带上这个前缀。

  • 原理和作用
    spring.webflux.base-path=/your-app (针对 WebFlux) 或者 server.servlet.context-path=/your-app (更多是传统 MVC 应用,但在某些混合场景下或因习惯可能也会用) 会改变你应用所有端点的基础URL。Swagger UI 的访问路径也会基于这个新的根路径。

  • 操作步骤
    检查 application.propertiesapplication.yml

    For application.properties:

    # 如果你的应用是纯 WebFlux
    spring.webflux.base-path=/api-gateway # 假设你的基础路径是 /api-gateway
    
    # server.servlet.context-path=/my-cool-app # 通常在Spring Boot 3 WebFlux项目中不直接使用这个来设置应用根路径
    

    如果设置了 spring.webflux.base-path=/api-gateway,那么你的 Swagger UI 应该通过 http://localhost:8080/api-gateway/swagger-ui.html 来访问。
    确认你的访问 URL 是否正确。

  • 额外建议
    统一配置。如果项目是纯 WebFlux,优先使用 spring.webflux.base-path。这个配置对 springdoc-openapi 的路径解析也有影响。

方案五:检查是否有 @EnableWebFlux 注解捣乱

这是一个容易被忽视的点。

  • 原理和作用
    @EnableWebFlux 注解的作用是导入 WebFluxConfigurationSupport,这会让你完全接管 WebFlux 的配置,Spring Boot 的 WebFlux 自动配置会失效。这意味着很多便利的默认设置,比如静态资源处理、Content Negotiation、编解码器等,都需要你手动配置。springdoc-openapi-ui 的自动配置也依赖于 Spring Boot 的 WebFlux 自动配置。

  • 操作步骤
    全局搜索你的项目代码,看看有没有哪个 @Configuration 类上加了 @EnableWebFlux

    // @Configuration
    // @EnableWebFlux // <--- 如果存在这个注解,尝试移除它
    // public class SomeWebConfig {
    //     // ... 可能有一些你自定义的 WebFlux 配置
    // }
    

    如果找到了,并且你不是非用它不可(比如为了实现一些非常底层的、自动配置无法满足的定制),尝试将它注释掉或删除。Spring Boot 的自动配置在大多数场景下都足够强大和灵活。

  • 额外建议
    绝大多数 Spring Boot 应用不需要 @EnableWebFlux。Spring Boot 的条件化自动配置 (spring-boot-autoconfigure) 旨在开箱即用。只有当你明确知道为什么需要完全控制,并且愿意承担手动配置所有相关组件的责任时,才使用它。移除它之后,确保测试所有 WebFlux 相关功能是否依然按预期工作。

方案六:依赖版本和冲突排查

虽然较为少见,但检查依赖总是没错的。

  • 原理和作用
    Maven 或 Gradle 的依赖解析可能引入不兼容的库版本。springdoc-openapi-starter-webflux-ui 自身依赖于特定版本的 swagger-ui Webjar。如果项目中其他依赖引入了另一个版本,或者与 Spring Boot 管理的某些基础 Web 库冲突,可能会出问题。

  • 操作步骤
    使用 Maven/Gradle 的依赖分析工具。
    对于 Maven:

    mvn dependency:tree
    

    仔细查看输出,特别关注与 swagger-uiwebjars-locator-core (如果 springdoc 用到的话) 或者 Spring Framework WebFlux 相关的库。确保 springdoc-openapi-starter-webflux-ui 的版本 2.2.0 和 Spring Boot 3.2.10 是兼容的(它们通常是)。springdoc 官方文档会说明其版本与 Spring Boot 版本的对应关系。2.2.0 是为 Spring Boot 3.x 设计的,这点应该没问题。

  • 额外建议
    考虑使用 Spring Boot 的 BOM (Bill of Materials) 来管理依赖版本,它可以帮助统一许多常用库的版本。springdoc-openapi 通常不包含在 Spring Boot 的主 BOM 里,所以你需要自己确保它的版本和 Boot 版本兼容。

排查这种问题,有点像侦探探案。通常从最可疑的“嫌犯”(Spring Security、路径配置)入手,逐步扩大排查范围。每做一个调整后,记得重启应用并清空浏览器缓存再试,避免旧缓存捣乱。一般情况下,通过上述几个方案的排查和调整,swagger-ui/index.html 应该就能乖乖地出现了。