不能共用一套cors中间件,因为v1和v2前端宿主域名不同(如https://legacy.example.com与https://app.example.com),共用白名单会导致漏放或宽放过头;且v2若启用credentials,access-control-allow-origin必须动态匹配origin、禁用通配符*,与v1固定域名策略冲突。

为什么不能共用一套 CORS 中间件
因为 v1 和 v2 的前端宿主可能完全不同:v1 前端跑在 https://legacy.example.com,v2 已迁移到 https://app.example.com;若强行用同一套 AllowedOrigins 白名单,要么漏放(v2 请求被拒),要么宽放过头(v1 的旧域名意外获得 v2 接口访问权)。更关键的是,v2 若开启 credentials(比如传 Cookie),就必须禁用通配符 *,且 Access-Control-Allow-Origin 必须严格等于请求头里的 Origin —— 这和 v1 可能用固定域名响应的逻辑冲突。
按路径前缀分离 CORS 配置(推荐)
利用路由库的子路由能力,在注册 /v1 和 /v2 时分别挂载不同策略的中间件。以 gorilla/mux 为例:
router := mux.NewRouter()
// v1 路由:只允许 legacy 域名,不支持 credentials
v1 := router.PathPrefix("/v1").Subrouter()
v1.Use(corsMiddleware([]string{"https://legacy.example.com"}))
v1.HandleFunc("/users", getUsersV1).Methods("GET")
// v2 路由:允许多个新域名,且明确支持 credentials
v2 := router.PathPrefix("/v2").Subrouter()
v2.Use(corsWithCredentials([]string{"https://app.example.com", "https://staging.app.example.com"}))
v2.HandleFunc("/users", getUsersV2).Methods("GET")
其中 corsWithCredentials 必须确保:
– Access-Control-Allow-Origin 是动态匹配而非写死
– 显式设置 Access-Control-Allow-Credentials: true
– 不在 OPTIONS 响应中调用 next.ServeHTTP
避免在中间件里解析 URL 路径取版本号
常见错误是写一个“万能”中间件,从 r.URL.Path 提取 /v1 或 /v2 再查表匹配策略。这会导致三个问题:
- 路由树不可见:
router.Walk打印不出真实路径,调试时无法确认/v2/users是否真的注册成功 - OPTIONS 预检可能误触发下游 handler(比如 v2 的预检被 v1 的中间件放行后,又进了 v1 的业务逻辑)
- 无法对不同版本单独配置日志、指标或熔断策略
正确做法是让版本成为路由拓扑的一部分,而不是运行时字符串判断。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
使用 gorilla/handlers 时如何分版本配置
gorilla/handlers.CORS 本身不支持 per-route 策略,但可以组合使用:
- 为
v1构建独立的handlers.CORS实例,传入专属AllowedOrigins - 为
v2构建另一个实例,并额外传入handlers.AllowCredentials() - 用
http.StripPrefix或子路由器把请求先分流到不同 handler 链
示例关键片段:
v1Handler := handlers.CORS(
handlers.AllowedOrigins([]string{"https://legacy.example.com"}),
handlers.AllowedMethods([]string{"GET", "POST"}),
)(&v1Router)
v2Handler := handlers.CORS(
handlers.AllowedOrigins([]string{"https://app.example.com"}),
handlers.AllowedMethods([]string{"GET", "POST", "PUT"}),
handlers.AllowCredentials(),
)(&v2Router)
// 最终合并
http.Handle("/v1/", v1Handler)
http.Handle("/v2/", v2Handler)
注意:http.Handle 的路径末尾必须带 /,否则 gorilla/handlers 无法正确截断前缀,导致 Origin 匹配失败。
真正容易被忽略的是预检请求的响应体——即使你设对了所有 header,如果 OPTIONS 返回了非空 body(比如 JSON 错误提示),某些浏览器会直接判定预检失败。务必保证 OPTIONS 响应只有状态码和 headers,body 为空。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










