accept header 做版本路由最稳妥:语义清晰、不污染 url、兼容缓存;须用 mime.parsemediatype 解析,按 vnd 格式提取版本并分组注册 handler,设置默认版本兜底,避免路径前缀与通配符冲突。

用 Accept Header 做版本路由最稳妥
Go 的 HTTP 路由本身不内置版本识别,得靠你自己解析请求头或路径。但 Accept 是 RESTful API 版本控制的推荐方式——它语义清晰、不污染 URL、兼容缓存策略,而且 net/http 拿起来就用。
常见错误是硬写 if r.Header.Get("Accept") == "application/vnd.myapi.v2+json",这会漏掉带参数的变体(比如 charset=utf-8),也扛不住大小写差异。
- 用
mime.ParseMediaType解析Accept,只取type/subtype和vnd.*.vN+*结构中的版本号 - 注册 handler 时按版本分组,比如
handlers["v1"] = handleV1,避免 if/else 堆叠 - 别忽略默认版本兜底:没匹配到任何
vnd类型时,走v1或返回406 Not Acceptable
mediaType, _, _ := mime.ParseMediaType(r.Header.Get("Accept"))
if strings.HasPrefix(mediaType, "application/vnd.myapi.v") {
version := strings.TrimPrefix(strings.Split(mediaType, "+")[0], "application/vnd.myapi.v")
// version = "2" or "1.5"
}
URL 路径带版本前缀(/v2/users)要小心路由冲突
很多团队图省事直接在路径里加 /v1/,结果发现 gorilla/mux 或 chi 的通配符规则一碰就炸:比如 /v1/{id} 和 /v1/status 冲突,或者升级后老客户端调 /v1/users/123,新服务却只认 /v2/users/123,没法平滑过渡。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 所有版本路径必须显式声明,别依赖通配符自动捕获;
chi里用r.Group(func(r chi.Router) { ... })隔离各版本路由树 - 版本前缀必须是固定字符串,禁用正则匹配(如
/v{version}/users),否则调试时根本看不出哪个 handler 被触发了 - 如果要用
http.ServeMux,得手动做前缀截断再转发,否则r.URL.Path里还带着/v2/,后续逻辑容易错乱
Content-Type 和 Accept 版本不一致时以谁为准?
没人规定必须一致,但实际中几乎都以 Accept 定响应格式和字段结构,Content-Type 只管请求体解析。强行让两者绑定会导致客户端发错 Content-Type 就直接 400,而其实你只想返回旧版 JSON。
- 解析请求体时只看
Content-Type是否能被json.Unmarshal或xml.Unmarshal处理,别校验其中是否含v2 - 响应版本完全由
Accept决定,哪怕请求体是v1格式,只要Accept: application/vnd.myapi.v2+json,就该返回v2字段 - 如果真需要双向版本对齐(比如 v2 不再接受 v1 的字段),那是在业务校验层做,不是在路由层拦截
用中间件统一提取版本信息,但别在中间件里写业务逻辑
有人把版本解析塞进中间件,然后在 handler 里反复读 r.Context().Value("version"),结果 debug 时发现 context 值是空的——因为中间件没正确调用 next.ServeHTTP,或者用了第三方中间件覆盖了 context。
- 中间件只做一件事:解析并存入 context,用自定义 key(比如
type versionKey struct{}),别用字符串 key 防止冲突 - handler 里用
ver, ok := r.Context().Value(versionKey{}).(string)判断,ok为 false 时立刻返回错误,别默许 fallback - 别在中间件里根据版本调不同 handler,那是路由的事;中间件只负责“让 handler 知道版本”,不负责“决定怎么处理”
版本控制最难的不是怎么写,而是怎么让所有人(前端、测试、运维)都清楚当前生效的是哪条路径、哪个 header、哪个默认值。一旦混用 URL 和 Header 版本,问题会藏得极深——比如 Nginx 缓存了 /v1 的响应,但客户端带了 v2 的 Accept,结果返回的是过期的 v1 数据。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










