跨域中间件必须在路由注册前调用r.Use(),否则不生效;AllowCredentials=true时AllowOrigins不能为"*";OPTIONS预检请求需显式处理并返回204;自定义中间件比gin-contrib/cors更灵活可控。

为什么跨域中间件注册顺序错了就失效
跨域中间件必须在任何路由注册之前调用 r.Use(),否则它根本不会被触发。常见错误是先定义路由组、再挂载中间件,比如:
r := gin.Default()-
api := r.Group("/api")→ 此时已注册内部路由逻辑 -
r.Use(CorsMiddleware())→ 中间件只对后续注册的路由生效,而/api已注册完毕,不走这个中间件
正确顺序只有一条:r.Use() 必须紧接在 r := gin.Default() 或 r := gin.New() 之后,且早于所有 r.GET、r.POST、r.Group()。
AllowCredentials = true 时不能写 "*" 作为 AllowOrigins
浏览器明确禁止:当响应头包含 Access-Control-Allow-Credentials: true 时,Access-Control-Allow-Origin 的值绝不能是 "*",否则整个响应会被直接丢弃,前端控制台报错 Response to preflight request doesn't pass access control check。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 若需支持带 Cookie 的请求(如登录态),必须显式列出可信域名:
[]string{"https://admin.example.com", "https://app.example.com"} - 若用
gin-contrib/cors,别填AllowOrigins: []string{"*"},应改用AllowOriginFunc动态校验:AllowOriginFunc: func(origin string) bool { return strings.HasPrefix(origin, "https://example.com") } - 开发阶段可临时设为具体本地地址:
"http://localhost:3000",避免上线后漏配
OPTIONS 预检请求必须放行且返回 204 或 200
浏览器在发 POST/PUT 等非简单请求前,会先发一个 OPTIONS 请求探路。如果中间件没处理好这个请求,后续业务请求永远卡住。
关键点:
- 必须在中间件里判断
c.Request.Method == "OPTIONS" - 不能只
c.AbortWithStatus(200),更推荐c.AbortWithStatus(204)(无响应体,语义更准确) - 不能在
c.Next()前写任何c.JSON或c.String,否则会冲突;c.AbortWithStatus是唯一安全出口 - 响应头如
Access-Control-Allow-Methods必须包含实际要用的动词,比如用了PUT就得写进列表,否则预检失败
自定义中间件比 gin-contrib/cors 更可控
gin-contrib/cors 封装虽快,但配置粒度粗、动态逻辑难介入。比如想根据请求头 Origin 查数据库白名单,或按路径开关跨域,它基本做不到。
手写中间件反而更轻量、易调试:
- 响应头全部自己控制,比如加
Access-Control-Expose-Headers暴露自定义字段 - 可嵌入日志:
log.Printf("CORS allowed for origin: %s", origin) - 可结合上下文做判断:只对
/api/路径启用,其他静态资源不设 CORS 头 - 避免引入额外依赖,减少构建体积和潜在兼容问题
真正麻烦的不是写中间件,而是漏掉预检处理、搞错注册顺序、或在带凭证时硬写 "*" —— 这三处出错,前端就彻底收不到响应。

















