URL路径前缀分组(如app.Group("/v1"))是Fiber实现接口版本控制唯一推荐方案,必须物理隔离路由、中间件、DTO及handler,禁用header或query参数等软性版本识别方式。

用 Fiber 实现接口版本控制,必须放弃“在 handler 里判断 X-API-Version”或“拼接 ?version=v2”这类软性方案——Fiber 没有内置版本路由,但它的 Group() 和中间件隔离能力足够支撑真正的物理版本切分。核心是:每个版本走独立子路由、结构体不共享、handler 不混写。
Fiber 的 Group() 必须用于版本前缀,不是装饰性写法
Fiber 的 app.Group("/v1") 是唯一能天然隔离路由语义、中间件作用域和日志路径的方式。别用 app.Get("/v1/users", ...) 手动写死路径——那会让 Group() 失去意义,后续加中间件或监控时无法按版本统一注入。
-
app.Group("/v1")返回的是新子路由实例,所有注册在其下的 handler 的c.Path()自动剥离前缀(比如请求/v1/users,handler 里c.Path()是/users),避免字符串解析逻辑 - 每个
Group()可挂专属中间件:v1.Use(loggingMiddleware)、v2.Use(jwtStrictMiddleware),互不影响 - 禁止跨 Group 复用 handler 函数名:不要让
v1.Get("/users", handleUsers)和v2.Get("/users", handleUsers)共用一个函数——哪怕逻辑一样,也得拆成v1.HandleUsers和v2.HandleUsers
DTO 结构体必须按版本声明,哪怕字段完全一致
Go 的 struct 是编译期契约,UserV1 和 UserV2 即使字段名、类型、tag 全部相同,也必须是两个独立类型。否则 v2 新增字段会通过 JSON 序列化“泄漏”到 v1 响应中,前端解析直接崩溃。
- v1 的响应结构体定义在
dto/v1/user.go,v2 在dto/v2/user.go;不要用type UserV2 UserV1别名,那只是掩耳盗铃 - 入参结构体同理:
v1.CreateUserReq和v2.CreateUserReq分开定义,哪怕当前只差一个Nickname字段 - 如果底层 service 层返回的是 domain model(如
domain.User),必须显式转换:json.Marshal(v1.UserFromDomain(u)),不能直接json.Marshal(u)
Fiber v3 的自定义 Ctx 不适合做版本分发逻辑
Fiber v3 支持 NewWithCustomCtx,但别把它当成版本路由的替代方案。自定义 Ctx 是为了扩展上下文能力(比如加 GetUserID()),不是为了在 Ctx 里塞 Version 字段再做 if 分支——这会把版本耦合进业务层,破坏可测试性和灰度能力。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
立即学习“go语言免费学习笔记(深入)”;
- 版本识别必须发生在路由层(即
Group()),而不是运行时从c.Path()或 header 解析 - 如果真要用 header 做兜底(例如兼容老 SDK),必须用中间件统一注入 context key:
c.Context().Set("version", "v2"),且仅限 fallback 场景;主路径永远走 URL - HTTP/2 环境下,某些 ingress(如 Envoy)默认不透传自定义 header,
X-API-Version极易静默丢失,不能作为主控手段
Swagger 文档和监控指标要绑定到 Group 实例
Fiber 本身不生成 OpenAPI,但配合 swaggo/swag 或 go-swagger 时,注释必须写在对应版本的 handler 上,且 @Router 路径要匹配 Group 前缀。否则文档里 /users 会同时显示 v1 和 v2 字段,失去契约意义。
- 在
v1.Get("/users", ...)上写// @Router /v1/users [get],不是/users - Prometheus metrics 标签(如
http_route)会自动捕获Group()前缀,所以/v1/users和/v2/users天然分开展示,无需额外打标 - 日志中间件里打印
c.Path()是不够的——要打完整原始路径:c.Request().URI().String(),否则 access log 里全是/users,看不出流量分布
最容易被忽略的一点:版本下线不是删代码,而是先停掉 Group() 注册、再等客户端全部升级、最后才清理 DTO 和 handler 目录。URL 路径一旦暴露出去,就等于契约已发布——它比任何文档都权威。

















