Buffalo 默认不处理跨域,需手动配置OPTIONS路由和WebSocket的upgrader.CheckOrigin;中间件无法覆盖预检请求和WebSocket升级,必须在路由层显式注册OPTIONS handler并在websocketHandler中初始化upgrader。

Buffalo 默认不处理跨域,必须手动配置中间件或响应头,否则前端发请求会直接被浏览器拦截 —— 它连 CORS 都不内置,更不会自动加 Access-Control-Allow-Origin。
为什么 Buffalo 的 CORS 配置不能靠全局中间件“一劳永逸”
Buffalo 的中间件链默认在路由匹配后、handler 执行前注入,但跨域预检(OPTIONS)请求可能根本没走到你写的 handler 里 —— 尤其当使用 app.GET("/ws", ...) 这类显式注册的 WebSocket 路由时,中间件甚至不会触发。
常见错误现象:Failed to fetch 或控制台报 No 'Access-Control-Allow-Origin' header is present,但后端日志里压根没打印出该请求。
- Buffalo 的
OPTIONS自动响应只对标准 REST 路由生效(比如app.Resource),对app.GET("/api/xxx")或自定义 handler 不自动 fallback - 若你在 handler 内部用
c.Response().Header().Set(...)加头,但忘了在OPTIONS分支里提前return,就会触发http: multiple response.WriteHeader calls - 使用
app.Use(middleware.CORS())时,它默认不处理OPTIONS预检,也不支持allowedCredentials: true场景下的*原点
正确做法:在路由层显式拦截 OPTIONS 并返回头
最稳妥的方式是绕过中间件,在 routes.go 里为每个需要跨域的路径手动注册 OPTIONS handler,并确保它在任何业务逻辑之前执行。
Buffalo框架 1.0.1 版本源码包下载,适合需要错误处理改进、依赖更新、render.Download 注释和 request logger 调整的 v1 项目。
示例(针对 /api/messages):
app.Options("/api/messages", func(c buffalo.Context) error {
c.Response().Header().Set("Access-Control-Allow-Origin", "http://localhost:3000")
c.Response().Header().Set("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS")
c.Response().Header().Set("Access-Control-Allow-Headers", "Content-Type,Authorization")
c.Response().Header().Set("Access-Control-Allow-Credentials", "true")
return c.Render(200, nil)
})
app.Get("/api/messages", func(c buffalo.Context) error {
c.Response().Header().Set("Access-Control-Allow-Origin", "http://localhost:3000")
c.Response().Header().Set("Access-Control-Allow-Credentials", "true")
// 后续业务逻辑...
return c.Render(200, render.JSON(m))
})
- 必须为每个需跨域的路径单独配
app.Options,不能只写app.Options("/**", ...)—— Buffalo 不支持通配符路由匹配OPTIONS -
Access-Control-Allow-Origin不能设为*同时又设Access-Control-Allow-Credentials: true,否则浏览器拒绝;必须写明确的源(如http://localhost:3000) - WebSocket 路由(如
/ws)不能走这套逻辑 —— 它需要在websocketHandler内部用upgrader.CheckOrigin控制,而不是加响应头
WebSocket 跨域必须用 upgrader.CheckOrigin,不是响应头
WebSocket 协议升级(Upgrade: websocket)阶段,浏览器只检查 Origin 请求头,不看 Access-Control-Allow-Origin。Buffalo 的 websocketHandler 若没初始化 upgrader,或者 CheckOrigin 返回 false,连接直接被拒绝,且无明确错误提示。
正确初始化方式(在 handler 内):
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
origin := r.Header.Get("Origin")
return origin == "http://localhost:3000" || origin == "https://prod.example.com"
},
Subprotocols: []string{"chat-v1"},
ReadBufferSize: 65536,
WriteBufferSize: 65536,
EnableCompression: true,
}
func websocketHandler(c buffalo.Context) error {
conn, err := upgrader.Upgrade(c.Response().Writer, c.Request(), nil)
if err != nil {
return errors.WithStack(err)
}
defer conn.Close()
// 后续连接管理...
}
-
CheckOrigin必须显式读取r.Header.Get("Origin")并比对,不能返回true—— 否则易被扫描器耗尽连接数 -
Subprotocols必须声明,某些 CDN(如 Cloudflare)会静默丢弃无子协议的Upgrade请求 - 不要在
websocketHandler里调c.Render()或c.Response().WriteHeader(),会破坏 upgrade 流程
真正容易被忽略的是:Buffalo 的 app.Serve() 启动后,所有非标准 HTTP 方法(尤其是 OPTIONS)和 WebSocket 升级,都不经过常规中间件生命周期 —— 你得在路由定义和 handler 内部两级控制,漏掉任意一层,跨域就失效。

















