Swagger文档在Kratos中打不开,根本原因是网关未显式放行/v3/api-docs和/swagger-ui/路径,导致CORS拦截、路由劫持或静态资源未正确嵌入,需在config.yaml中单独配置路由与静态资源handler并验证透传。

Swagger 文档在 Kratos 项目中打不开,大概率不是 Swagger 本身没启,而是它生成的 OpenAPI 文档路径(/v3/api-docs)或 UI 嵌入逻辑被网关/静态资源路由劫持了——Kratos 默认用 Gateway 统一入口,而 Gateway 的 CORS 和路由规则会直接拦截或覆盖文档请求。
为什么 /swagger-ui/index.html 能加载但接口列表为空
这是最典型的症状:UI 页面能打开,但控制台报错 Failed to fetch API definition 或请求 /v3/api-docs 返回 404/500。根本原因是:
- Kratos Gateway 默认启用
cors和router中间件,若未显式放行/v3/api-docs和/swagger-ui/路径,这些请求根本到不了下游 service - GoWind Admin 等基于 Kratos 的框架虽内置 Swagger UI,但它的
embed.FS是嵌在 gateway 进程里的,不是 downstream service 提供的;如果 gateway 的config.yaml没配好静态资源 handler,/swagger-ui/下的 JS 就会 404 - 下游 service 即使启用了
swag init和http.HandleFunc("/v3/api-docs", ...),网关也不会自动透传该路径——它只按config.yaml里routes定义的service_id转发
config.yaml 必须显式声明 Swagger 文档和 UI 的路由
不能依赖下游 service 自己暴露路径。Gateway 层要主动把文档和 UI 当作两个独立服务来代理或嵌入:
- 若用 GoWind Admin 这类集成方案:确认
cmd/gateway/config.yaml的middlewares.static已启用,且dir指向正确嵌入路径(如./swagger-ui),否则/swagger-ui/index.html会 404 - 若下游 service 自行提供
/v3/api-docs:必须在routes下加一条规则,例如- pattern: /v3/api-docs/**; service_id: user-service,否则网关不会转发 - 禁止在下游 service 里重复注册
/swagger-ui/静态路由——Kratos Gateway 不支持多层静态资源 handler 嵌套,会冲突 - 检查
allowOrigins是否含当前前端域名;若设了allowCredentials: true却写["*"],浏览器静默丢弃响应,连控制台都看不到错误
验证网关是否真正透传了 /v3/api-docs
别信浏览器,用 curl 直接测网关地址:
curl -v http://localhost:9000/v3/api-docs
预期返回 200 和 JSON 内容。如果返回 404,说明路由没配;如果返回 204 或空响应,大概率是网关的 cors 中间件拦截了 OPTIONS 后没放行 GET;如果返回 500,看 gateway 日志里是否报 failed to dial service user-service——注册中心里 service 名和 routes.service_id 对不上
下游 service 的 Swagger 初始化容易漏掉关键点
即使网关配对了,下游 service 的 OpenAPI 文档也得能生成:
- 确保
swag init在构建前执行,且docs/docs.go被正确 import(Kratos 项目常用//go:embed docs,但必须保证 embed 路径和 HTTP handler 路径一致) - handler 注册不能写成
http.HandleFunc("/v3/api-docs", func...)—— Kratos service 用的是http.ServeMux,应通过server.HTTPServer的Handle方法挂载,否则不生效 - Protobuf 生成的 OpenAPI 需依赖
protoc-gen-openapi插件,若api/helloworld/v1/helloworld.proto里没加option (openapiv3.grpc_service) = true;,生成的 JSON 就不含 gRPC 映射
最常被忽略的是:网关和下游 service 的健康检查路径(/healthz)通常能通,但文档路径是独立路由,不共享同一套配置逻辑——得单独盯住每一段。











