最近在SpringBoot 3.3.4项目中集成Springdoc(Swagger 3)时遇到了一个典型问题:API文档的JSON端点可以正常访问,但Swagger UI界面却返回404错误。具体表现为:
/v3/api-docs 能正确返回OpenAPI规范的JSON数据/swagger-ui/index.html 却返回404 Not Found这种情况在SpringBoot 3.x + Springdoc的组合中并不少见,特别是当我们只添加了基础依赖而没有进行额外配置时。问题的根源在于SpringBoot 3.x对静态资源处理机制的调整,以及Springdoc的UI资源默认路径发生了变化。
提示:Springdoc是Swagger的继任者,它原生支持OpenAPI 3规范,与SpringBoot 3.x有更好的兼容性。但在某些情况下仍需要手动配置资源路径。
确保你的开发环境满足以下条件:
在pom.xml中,我们只需要添加一个核心依赖:
xml复制<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.6.0</version>
</dependency>
这个依赖实际上包含了两个关键部分:
springdoc-openapi-starter-webmvc-core:提供OpenAPI规范生成功能springdoc-openapi-starter-webmvc-ui:提供Swagger UI界面支持理论上,仅添加这个依赖就应该能同时获得API文档和UI界面。但在实际项目中,UI部分经常需要额外配置。
当遇到UI无法访问时,建议按以下步骤排查:
启用调试日志:在application.properties中添加
properties复制logging.level.root=debug
检查资源加载:启动应用后,观察控制台日志中关于静态资源加载的部分
直接访问资源:尝试访问/webjars/swagger-ui/5.17.14/index.html(版本号可能不同)
问题的核心在于SpringBoot 3.x默认不会自动暴露Swagger UI所需的静态资源。虽然依赖中的资源确实存在于classpath中(位于META-INF/resources/webjars/swagger-ui/目录下),但Spring的ResourceHandler没有自动配置这些路径。
我们需要手动添加资源处理器配置。以下是完整的解决方案:
java复制@SpringBootApplication
public class SpringbootApplication implements WebMvcConfigurer {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 配置 Swagger UI 资源路径
registry.addResourceHandler("/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/swagger-ui/5.17.14/");
}
}
关键点说明:
addResourceHandler:定义URL访问路径模式addResourceLocations:指定实际资源在classpath中的位置5.17.14需要与你实际引入的swagger-ui版本一致硬编码版本号不是最佳实践。我们可以通过Maven属性动态获取:
java复制@Value("${springdoc.swagger-ui.version}")
private String swaggerUiVersion;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
String uiPath = "classpath:/META-INF/resources/webjars/swagger-ui/" + swaggerUiVersion + "/";
registry.addResourceHandler("/swagger-ui/**").addResourceLocations(uiPath);
}
然后在pom.xml中定义属性:
xml复制<properties>
<springdoc.swagger-ui.version>5.17.14</springdoc.swagger-ui.version>
</properties>
对于不同环境,你可能需要:
可以通过Profile来实现:
java复制@Profile("!prod")
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 配置代码
}
如果你想使用自定义路径而非默认的/swagger-ui/,可以这样配置:
java复制registry.addResourceHandler("/api-docs/ui/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/swagger-ui/5.17.14/");
同时需要配置Springdoc使用这个路径:
properties复制springdoc.swagger-ui.path=/api-docs/ui/index.html
打开浏览器访问:http://localhost:8080/swagger-ui/index.html
(端口号根据你的实际配置调整)
在Swagger UI顶部的输入框中,填入你的API文档地址:
code复制/v3/api-docs
或完整路径:
code复制http://localhost:8080/v3/api-docs
点击"Explore"按钮加载API文档
成功加载后,你应该能看到:
问题现象:
解决方案:
bash复制mvn dependency:tree
问题现象:
解决方案:
java复制@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/v3/api-docs/**");
}
};
}
问题现象:
解决方案:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
}
可以在application.properties中定制UI外观:
properties复制springdoc.swagger-ui.operationsSorter=alpha
springdoc.swagger-ui.tagsSorter=alpha
springdoc.swagger-ui.docExpansion=none
springdoc.swagger-ui.filter=true
springdoc.swagger-ui.tryItOutEnabled=true
对于大型项目,可以考虑API分组:
java复制@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.group("public")
.pathsToMatch("/public/**")
.build();
}
@Bean
public GroupedOpenApi adminApi() {
return GroupedOpenApi.builder()
.group("admin")
.pathsToMatch("/admin/**")
.build();
}
对于生产环境(如果必须启用):
properties复制# 禁用不必要的功能
springdoc.cache.disabled=false
springdoc.model-and-view-allowed=false
springdoc.auto-tag-classes=false
除了Java配置,也可以通过properties文件配置资源路径:
properties复制spring.web.resources.static-locations[0]=classpath:/META-INF/resources/webjars/swagger-ui/5.17.14/
spring.web.resources.static-locations[1]=classpath:/static/
但这种方式不够灵活,且可能影响其他静态资源。
Springdoc提供了一些自动配置属性:
properties复制springdoc.swagger-ui.url=/v3/api-docs
springdoc.swagger-ui.validatorUrl=
但这些属性主要控制UI行为,不解决资源路径问题。
| 方案 | 优点 | 缺点 |
|---|---|---|
| Java配置 | 灵活,可编程控制 | 需要编写代码 |
| Properties配置 | 简单,集中管理 | 不够灵活 |
| 升级Springdoc版本 | 可能自动解决 | 需要测试兼容性 |
在多个生产项目中实施Springdoc后,总结出以下经验:
版本一致性:确保团队所有成员使用相同的Springdoc版本,避免因版本差异导致的问题。
资源缓存:在开发阶段可以禁用缓存以便实时查看更改:
properties复制spring.web.resources.cache.period=0
API文档质量:结合Javadoc和Springdoc注解提升文档质量:
java复制@Operation(summary = "创建用户", description = "创建一个新用户账号")
@ApiResponse(responseCode = "201", description = "用户创建成功")
public ResponseEntity<User> createUser(@RequestBody UserDto userDto) {
// 方法实现
}
安全考虑:即使是在内网环境,也建议为Swagger UI添加基本认证:
java复制http.authorizeHttpRequests(auth -> auth
.requestMatchers("/swagger-ui/**").authenticated()
)
.httpBasic(withDefaults());
性能监控:对于高频访问的API文档端点,建议添加监控:
java复制@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config().commonTags(
"application", "api-docs"
);
}
当遇到问题时,以下工具和技巧可能帮到你:
依赖分析:
bash复制mvn dependency:tree -Dincludes=org.springdoc
资源定位:
java复制Resource resource = new ClassPathResource("META-INF/resources/webjars/swagger-ui/5.17.14/index.html");
System.out.println("Resource exists: " + resource.exists());
请求追踪:
在application.properties中启用完整请求日志:
properties复制logging.level.org.springframework.web=TRACE
logging.level.org.springframework.security=DEBUG
版本兼容性检查:
定期查看Springdoc官方文档的兼容性矩阵,确保SpringBoot版本与Springdoc版本匹配。
备用UI方案:
如果标准UI仍然有问题,可以尝试ReDoc替代:
xml复制<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.6.0</version>
</dependency>
然后访问/redoc.html