Revel 框架需手动配置 CORS:必须显式注册 OPTIONS 路由、手写响应头、精确匹配 Origin(不可用 *)、开启 Access-Control-Allow-Credentials、确保中间件顺序不覆盖头,并在 Nginx 中透传 Origin 头。

Revel 框架默认不自动处理 CORS,前后端分离时只要前端(如 http://localhost:3000)和后端(如 http://localhost:9000)协议、域名或端口任一不同,浏览器就会拦截响应——不是请求没发出去,而是响应被拦在了浏览器层,控制台报 Blocked by CORS policy: No 'Access-Control-Allow-Origin' header。
Revel 中必须手动注册 OPTIONS 路由并写死响应头
Revel 不像 Spring Boot 那样内置 CorsConfiguration 或全局过滤器,也不支持注解式跨域。它依赖显式路由 + 手动设置响应头,尤其对预检请求(OPTIONS)必须单独处理,否则 PUT/DELETE 或带 X-Token 头的请求直接失败。
- 所有可能触发预检的路径(比如
PUT /api/articles/:slug、DELETE /api/profiles/:username/follow)都得配对应的OPTIONS路由 - 不能只在控制器里用
c.Response.Out.Header().Set(),因为预检请求根本不会进控制器,必须在路由层拦截 - 推荐在
app/init.go的init()函数里注册,例如:
router.GET("/api/articles/:slug", app.Articles.Show)
router.PUT("/api/articles/:slug", app.Articles.Update)
router.OPTIONS("/api/articles/:slug", app.OptionsHandler) // ← 必须加这一行
router.DELETE("/api/articles/:slug", app.Articles.Delete)然后在 app/OptionsHandler 里统一写头:
func (c App) OptionsHandler() revel.Result {
c.Response.Out.Header().Set("Access-Control-Allow-Origin", "http://localhost:3000")
c.Response.Out.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Response.Out.Header().Set("Access-Control-Allow-Headers", "Content-Type, X-Token, Authorization")
c.Response.Out.Header().Set("Access-Control-Allow-Credentials", "true")
return nil
}Access-Control-Allow-Origin 不能写 * 当带 credentials 时
前端若用了 credentials: 'include'(比如要传 Cookie 或 Auth Token),后端就不能设 Access-Control-Allow-Origin: "*",否则浏览器直接拒绝响应。Revel 里必须精确匹配 Origin 值,否则白配。
- 开发阶段可硬编码为
"http://localhost:3000",但上线后需动态读取请求头Origin并校验白名单 - 别漏掉
Access-Control-Allow-Credentials: "true",否则前端即使发了 Cookie,后端也收不到 - 如果后端还用了 Session 或 JWT 存在 Cookie,
credentials必须开,且前端 fetch 要显式写{ credentials: 'include' }
Revel 中间件顺序错会导致 CORS 头被覆盖
Revel 的中间件执行顺序很关键。如果你在 app/init.go 里注册了自定义中间件(比如日志、认证、压缩),而它在 CORS 头写入之后又调用了 next(c),某些中间件(尤其是压缩类)可能重置响应头,导致 Access-Control-Allow-Origin 消失。
- 确保
OPTIONS路由的 handler 在所有中间件链最前,或至少在任何可能修改响应头的中间件之前执行 - 避免在中间件里调用
c.Response.Out.Header().Del()或重复Set(),Revel 的 Header 是 map,后写会覆盖前写 - 调试时可用
c.Response.Out.Header().Get("Access-Control-Allow-Origin")打印确认是否还在
生产环境务必禁用通配符 Origin,且检查 Nginx 是否透传了 Origin 头
本地跑 Revel 直连没问题,但上生产往往加了 Nginx 反向代理。这时候容易忽略两点:一是 Nginx 默认不透传 Origin 请求头,二是你写的白名单逻辑拿不到原始 Origin,全变成 localhost 或内网 IP。
- Nginx 配置里必须加:
proxy_set_header Origin $http_origin; - Revel 后端读 Origin 时要用
c.Request.Header.Get("Origin"),而不是硬编码 - 别信“先用 * 测试通了再说”,带凭证的场景下,* 和 true 是互斥的,线上一旦开了 credentials 就必须关 *
Revel 的跨域难点不在代码量,而在每个环节都得手动抠:路由要显式注册、头要手写、Origin 要校验、中间件顺序要卡准、Nginx 配置要补全——少一步,F12 控制台就红一片。


















