十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SpringBoot3 Swagger UI 打不开?从排查到解决的完整指南

SpringBoot3 Swagger UI 打不开?从排查到解决的完整指南 最近用 SpringBoot3 升级一个老项目时踩了个非常典型的坑/v3/api-docs这个 OpenAPI 的 JSON 接口访问得干干净净、一点问题没有可/swagger-ui.html就是打不开直接 404。换成/swagger-ui/index.html依旧白页折腾了整整两个晚上才把根因彻底理顺。这个现象在 Springdoc 2.x SpringBoot3 这个组合里其实非常常见。网上能搜到的帖子要么只给一个答案要么就是不区分具体场景抄过来也没用。这篇文章我直接把自己踩过的坑、每一步排查的思路、以及最终验证可用的完整配置全部摆出来。文章适合正好卡在“api-docs 能访问但 UI 打不开”的朋友也适合刚准备给 SpringBoot3 项目接 Swagger 文档、想少走弯路的人。1. 先复现现象搞清楚“不能访问”到底是哪种不能访问1.1 我遇到的现场我的环境是SpringBoot 3.2.x JDK 17 springdoc-openapi-starter-webmvc-ui 2.3.0内嵌 Tomcat 部署。项目启动后所有业务接口正常但访问文档页时出现了非常分裂的状态curl -i http://localhost:8080/v3/api-docs # 200返回一大段 OpenAPI JSON curl -i http://localhost:8080/swagger-ui.html # 404Whitelabel Error Page curl -i http://localhost:8080/swagger-ui/index.html # 404同样打不开浏览器直接访问/swagger-ui.html出现的是 Spring Boot 默认的错误页Tomcat 日志里也没有任何明显的异常堆栈。这个现象最迷惑人的地方在于既然/v3/api-docs能正常输出说明 Springdoc 的核心功能已经生效了为什么 UI 偏偏不行这里需要先纠正一个误区无法访问 UI 不等于 Springdoc 没生效它只是说明 Swagger UI 这一层没有正常工作。如果你也遇到同样情况先不要急着怀疑版本更不要盲目重装依赖而是要按下面第 2 节的分析搞清楚 api-docs 和 swagger-ui 在 Spring MVC 里完全就是两条不同的路由。1.2 常见的三种“HTML 无法访问”表现根据我后来在网上翻帖子和自己复现的经验Springdoc 场景下的“HTML 无法访问”基本有三类表现典型状态码大概率原因页面直接 404404依赖缺 UI 包、静态资源映射被改、路径不对页面返回 403 或被重定向到登录页403 / 302Spring Security 拦截了 swagger-ui 路径页面返回 200 但白屏或显示一堆 HTML 源码200JS/CSS 加载失败、响应 Content-Type 不对、代理配置问题这几种情况的原因和解决办法完全不同。你只有先确认自己属于哪一种再去搜解决方案才不会像我一样绕远路。2. 为什么 api-docs 能访问而 html 不行两类路由的差异2.1/v3/api-docs走的是 Controller 映射Springdoc 在启动时会向 Spring 容器注册一个 OpenAPI 相关的RestController把/v3/api-docs映射到具体的 Controller 方法上。换句话说这个地址本质上是一个普通的 MVC 接口和你自己写的RestController没有任何区别。只要 springdoc 的核心 API 库被正确加载/v3/api-docs就会作为 DispatcherServlet 管理的一个普通端点存在。它不依赖任何静态资源也不依赖 webjars所以哪怕 Swagger UI 的资源包完全没有这个接口依然能正常返回 JSON。这也是为什么很多人看到 api-docs 正常就不去怀疑依赖问题的原因。我当时排查时也犯了这个错误一直在 api-docs 的逻辑里打转完全没意识到 UI 是另一套资源。2.2/swagger-ui.html走的是静态资源映射Swagger UI 本质上是一个纯前端项目由一堆 HTML、CSS、JavaScript 文件组成。这些文件被打包在 webjars 依赖里路径通常位于classpath:/META-INF/resources/webjars/swagger-ui/。Springdoc 的自动配置会在启动时注册一个ResourceHandler把/swagger-ui/**这个 URL 路径映射到上面的 webjars 目录。访问/swagger-ui.html时Springdoc 还会做一次转发把它重定向到/swagger-ui/index.html。所以在 Spring MVC 内部这两条链路是这样的路径类型依赖可能被影响的因素/v3/api-docsController 方法springdoc 核心库DispatcherServlet 是否可用/swagger-ui.html静态资源映射springdoc UI 库 webjars 资源静态资源配置、Security、依赖版本、代理这种差异直接解释了一个关键结论api-docs 能访问只说明核心库没问题它无法证明 UI 所依赖的资源包、静态资源映射和访问控制没有问题。2.3 实际排查时要把两条链路分开看我后来总结出一个很实用的排查原则把“API 数据路径”和“UI 资源路径”当成两个独立的问题去看。如果/v3/api-docs挂了说明 OpenAPI 数据生成有问题通常和依赖版本、配置冲突有关。如果发现/v3/api-docs正常但/swagger-ui.html挂了那就把注意力放到静态资源、安全拦截、反向代理这三件事上。这样排查范围一下子缩小了很多也避免在错误的层次上浪费时间。3. 逐个排查大多逃不过这 5 类原因3.1 依赖引错或只引了一半这是最常见的原因没有之一。很多人用 SpringBoot3 时只引入了下面这个依赖dependency groupIdorg.springdoc/groupId artifactIdspringdoc-openapi-starter-webmvc/artifactId version2.3.0/version /dependency注意springdoc-openapi-starter-webmvc这个包只包含 OpenAPI 核心能力和自动配置它不包含 Swagger UI 的前端静态资源。所以它能保证/v3/api-docs正常但/swagger-ui.html就会 404。如果你需要 UI 页面必须引入带ui后缀的完整包dependency groupIdorg.springdoc/groupId artifactIdspringdoc-openapi-starter-webmvc-ui/artifactId version2.3.0/version /dependency另外还要注意几点如果是 WebFlux 项目要引springdoc-openapi-starter-webflux-ui而不是 webmvc 版本。starter-webmvc-ui这个依赖已经包含了 api 核心能力不需要额外再引starter-webmvc否则可能出现重复的 OpenAPI 资源在某些版本下反而会引发奇怪问题。检查一下 pom 里是否残留了 Springfoxspringfox-swagger2相关依赖Springdoc 和 Springfox 同时存在时不一定会直接报错但争抢路径的情况非常坑。我在自己项目里最终定位到的原因就是这个之前只引了starter-webmvc压根没引入 UI 包。属于那种“看起来简单但一旦漏掉就怎么调都调不出来”的问题。3.2 Spring Security 把页面拦截了如果你项目里引入了spring-boot-starter-security那依赖没问题也可能打不开 UI。Spring Security 在 SpringBoot3 中默认会拦截所有请求未认证访问/swagger-ui.html时通常会被重定向到登录页或者因为在配置里没放开这些路径而返回 403/404。SpringBoot3 使用的是 Spring Security 6.x配置风格已经从旧的WebSecurityConfigurerAdapter换成SecurityFilterChain。如果你在网上搜到的是过时的写法直接复制会报错。一个标准的放行配置像下面这样Configuration public class SecurityConfig { Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(auth - auth .requestMatchers( /swagger-ui.html, /swagger-ui/**, /v3/api-docs/**, /v3/api-docs.yaml, /webjars/** ).permitAll() .anyRequest().authenticated() ); return http.build(); } }这里几个关键点用requestMatchers而不是旧版的antMatchers后者在 Spring Security 6 里已经移除。放行路径不仅要包含/swagger-ui.html还要放行/swagger-ui/**因为index.html加载之后还会请求大量 JS/CSS 文件这些都属于/swagger-ui/**路径。/v3/api-docs/**也要放行。否则可能出现 UI 页面能打开但页面一直转圈控制台报“Failed to load API definition”的尴尬情况。如果你配了 Spring Security 的链路建议用curl -i看一眼是否有 302 跳转如果有基本就是被安全拦截了。3.3 自定义静态资源配置把 swagger-ui 的路由冲掉这种情况相对隐蔽。比如有些项目需要在/static/目录下放静态资源会自定义WebMvcConfigurerConfiguration public class WebConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/**) .addResourceLocations(classpath:/static/); } }这段代码本身没太大问题但如果你注册的 handler 路径过于宽泛比如/**在某些情况下会影响 Springdoc 自动注册的/swagger-ui/**映射。更常见的是在application.yml里做了这种配置spring: mvc: static-path-pattern: /static/**这行配置把 Spring MVC 默认的静态资源匹配模式从原来的/**改成了/static/**。而 Swagger UI 的资源位于 webjars 下对应的默认映射是/webjars/**。一旦你改了全局静态路径webjars 的默认映射可能就不生效了导致/swagger-ui/**的资源全部 404。解决办法有两个方向尽量不要修改spring.mvc.static-path-pattern除非你非常清楚自己在做什么。如果项目确实需要自定义静态路径可以在WebMvcConfigurer里显式补上 webjars 的映射registry.addResourceHandler(/webjars/**) .addResourceLocations(classpath:/META-INF/resources/webjars/);即使全局静态路径被改了只要补上这一条/swagger-ui/**所需要的 webjars 资源就能重新被路由到。3.4 版本不兼容SpringBoot3 必须用 Springdoc 2.x这是个坑中坑。SpringBoot2 时代大家用的 Springdoc 是org.springdoc:springdoc-openapi-ui:1.6.x这个版本的内部实现基于javax.*命名空间。SpringBoot3 全面迁移到了jakarta.*命名空间如果你在 SpringBoot3 项目里直接用了 1.x 的 springdoc很可能启动阶段就报ClassNotFoundException或NoClassDefFoundError甚至静默地导致 UI 不生效。正确做法是引入 2.x 系列的依赖dependency groupIdorg.springdoc/groupId artifactIdspringdoc-openapi-starter-webmvc-ui/artifactId version2.6.0/version /dependency版本号可以用目前最新的 2.x 稳定版。这里还要提醒一下检查 pom 时不要只看显示名称要用mvn dependency:tree | grep springdoc看一下实际解析出来的版本。曾经见过父级 BOM 或者传递依赖把 springdoc 版本覆盖回 1.x 的情况这种问题在配置里根本看不出来。3.5 反向代理/Nginx 导致页面资源加载不出来生产环境普遍会用 Nginx 做反向代理。这种情况下有个典型现象后端直接访问/swagger-ui.html是好的但通过 Nginx 域名访问就是白屏按 F12 看到一堆 JS/CSS 请求 404。原因多半是 Nginx 的 location 只转发到了根路径后续浏览器请求/swagger-ui/swagger-ui.css、/swagger-ui/index.css这些静态资源时没有被转发到后端或者被另一个静态服务拦截了。一个基础的 Nginx 配置示例server { listen 80; server_name your-domain.com; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /swagger-ui/ { proxy_pass http://127.0.0.1:8080/swagger-ui/; } location /v3/api-docs { proxy_pass http://127.0.0.1:8080/v3/api-docs; } }如果后端项目设置了server.servlet.context-path比如/demo那 Nginx 的转发也要对应加上前缀否则/swagger-ui.html访问的链路是通不上的。还有一类不太容易想到的问题Nginx 没有配置 MIME 类型表导致 HTML、CSS、JS 文件返回时Content-Type是application/octet-stream。浏览器拿到这种响应后不会按页面渲染而是直接当成文件下载或者显示成纯文本源码。这种情况在html 文件无法预览的搜索结果里非常常见。解决办法是在 Nginx 的http块里加上include /etc/nginx/mime.types;加了之后重启 Nginx再访问 swagger-ui 页面如果之前的症状是“显示一堆 HTML 标签源码”这个操作大概率能解决。3.6 页面返回 200 但一直转圈或加载不出接口列表还有一种情况/swagger-ui.html返回 200页面也出来了但中间位置一直转圈控制台提示 “Failed to load API definition”。这是 Swagger UI 页面启动后会自己去请求一个 OpenAPI 数据源默认就是/v3/api-docs。如果这个地址被安全框架拦截、被代理规则挡住、或者因为路径前缀不对导致 404UI 就会一直卡在加载中。排查方向单独访问一下/v3/api-docs确认返回的是 200 和 JSON 内容。如果前面配了 Spring Security检查/v3/api-docs/**是否放行。如果项目有 context-path 或网关前缀检查浏览器地址栏里请求的 api-docs 路径是否正确。还有一种可能是 Springdoc 配置了自定义urls或config-url指向了一个根本不存在的地址。比如设置springdoc.swagger-ui.config-url: /foo/bar.json而/foo/bar.json又没有对应的接口就会导致 UI 加载失败。4. 直接给一套能用的完整配置如果你不想逐条排查可以直接参照这一套配置。这是我目前用的组合在 SpringBoot3 项目里实测可跑。4.1 Maven 依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springdoc/groupId artifactIdspringdoc-openapi-starter-webmvc-ui/artifactId version2.3.0/version /dependency如果项目里已经有 spring-boot-starter-security保留即可不需要为了 swagger 去掉安全框架。4.2 application.yml 配置springdoc: api-docs: enabled: true path: /v3/api-docs swagger-ui: enabled: true path: /swagger-ui.html operations-sorter: method tags-sorter: alpha display-request-duration: true这里几个配置项的含义springdoc.api-docs.path就是 OpenAPI JSON 的访问路径默认是/v3/api-docs可以不写。springdoc.swagger-ui.path是 UI 页面入口地址默认是/swagger-ui.html。注意这里的 path 虽然看似是 HTML 文件但实际还是由 Springdoc 转发到/swagger-ui/index.html。operations-sorter和tags-sorter只是 UI 展示层面的排序规则不写不影响功能。如果不想让 Swagger UI 自动加载默认的 petstore 示例可以加上springdoc: swagger-ui: disable-swagger-default-url: true否则打开页面后右上角可能有一个指向 petstore 的默认下拉项虽然不影响使用但第一次看会觉得有点突兀。4.3 Spring Security 放行配置如果引入了 Spring Security参考下面的配置Configuration public class SecurityConfig { Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(auth - auth .requestMatchers( /swagger-ui.html, /swagger-ui/**, /v3/api-docs/**, /v3/api-docs.yaml, /webjars/** ).permitAll() .anyRequest().authenticated() ); return http.build(); } }如果项目还配置了 OAuth2 资源服务器或自定义过滤器放行路径要确保在过滤器链中不会被前置过滤器拦截。4.4 Nginx 反向代理配置生产环境用 Nginx 时参考如下server { listen 80; server_name your-domain.com; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location /swagger-ui/ { proxy_pass http://127.0.0.1:8080/swagger-ui/; } location /v3/api-docs { proxy_pass http://127.0.0.1:8080/v3/api-docs; } }同时确认http块里引入了mime.types避免静态资源 Content-Type 错误。5. 实战排查步骤三步定位问题5.1 用 curl 分层判断别急着开浏览器遇到这种问题我强烈建议先用命令行工具做分层判断而不是直接打开浏览器看。浏览器有缓存、有 JS 执行环境反而干扰定位。第一步确认核心 API 数据是否正常curl -I http://localhost:8080/v3/api-docs如果返回 200说明 Springdoc 核心链路没问题。第二步确认 UI 入口地址的状态curl -I http://localhost:8080/swagger-ui.html curl -I http://localhost:8080/swagger-ui/index.html观察返回的状态码和 Location 头。第三步确认静态资源是否可访问curl -I http://localhost:8080/swagger-ui/swagger-ui.css这个请求如果 404问题基本可以锁定在依赖或静态资源映射上。5.2 用依赖树检查 Jar 包mvn dependency:tree | grep springdoc输出里应该能看到springdoc-openapi-starter-webmvc-ui以及相关的swagger-uiwebjar。如果看到的是springdoc-openapi-ui而且是 1.x 版本说明依赖引错了如果只有springdoc-openapi-starter-webmvc而没有带ui的包说明 UI 资源压根没进类路径。这个命令也能顺带排查是否同时存在 Springfox 等冲突依赖。5.3 临时关闭 Security 验证如果你怀疑是 Spring Security 搞的鬼最快速的方式是临时排除安全自动配置验证一下SpringBootApplication(exclude {SecurityAutoConfiguration.class})改完以后重新启动再访问/swagger-ui.html。如果此时能正常打开说明问题就出在安全放行配置上把配置按第 4.3 节调整即可。如果排除后依然打不开那问题基本不在安全层继续查依赖和静态资源映射。这个操作只能用在本地验证不要在生产环境直接关闭安全配置。5.4 检查自动配置是否生效SpringBoot3 中可以通过启动时加--debug参数来打印自动配置报告mvn spring-boot:run -Ddebug然后在输出里搜索Swagger或springdoc看相关的SwaggerUiWebMvcConfigurer是否处于matched状态。如果看到的是negative match说明某些条件没有满足顺着提示去看缺少了什么。这个命令输出的信息量非常大排查过 Spring Boot 自动配置问题的人应该不陌生但新手看了可能会觉得太乱。其实只需要关注包含springdoc、swagger、webjars的这些片段即可。6. 常见问题速查表最后整理一个速查表方便大家对照自己的现象直接定位不用从头再读一遍现象可能原因解决方式/v3/api-docs返回 200/swagger-ui.html返回 404只引了starter-webmvc没有引 UI 包换成springdoc-openapi-starter-webmvc-ui/swagger-ui.html返回 403 或跳转登录页Spring Security 拦截放行/swagger-ui/**、/v3/api-docs/**、/webjars/**页面 200 但白屏F12 里 JS/CSS 404静态资源映射被覆盖或代理未转发/swagger-ui/**检查static-path-pattern补 Nginx location页面显示 HTML 源码Nginx 未加载mime.types在http块里include mime.types;启动报javax.servlet相关错误Springdoc 1.x 与 SpringBoot3 不兼容升级到 Springdoc 2.x页面能打开但一直转圈控制台报 API definition 加载失败/v3/api-docs被安全拦截或路径不对放行 api-docs检查 context-path 和代理前缀/swagger-ui.html跳转/swagger-ui/index.html后还是 404webjars 资源缺失或浏览器缓存强刷缓存检查依赖树直接访问/swagger-ui/index.html7. 总结一下我的排查顺序这个问题真正难的地方不是解决方案本身而是它背后的故障现象会诱导你在错误的方向上反复试探。我现在遇到类似问题会严格按这个顺序来第一步用curl看/v3/api-docs和/swagger-ui.html的状态码先确认到底是哪一层挂了。第二步跑mvn dependency:tree | grep springdoc确认 UI jar 是否真的在依赖里。第三步如果涉及安全框架临时 exclude 掉 Security 自动配置做验证。第四步如果以上都正常再考虑代理、路径前缀、Content-Type 这些外部因素。另外还有一个小建议在项目里显式配置springdoc.api-docs.enabled和springdoc.swagger-ui.enabled不要完全依赖默认值。这样后续排查时至少能确定配置开关没有被隐式改掉。如果你接口数量多还可以配合springdoc.group-configs做接口分组Swagger UI 左上角会多一个分组下拉框文档管理体验会好很多。希望这篇踩坑记录能帮你少熬一个晚上。
返回列表