Revel 框架不内置 WebSocket 支持,因其 Controller 生命周期绑定 HTTP 请求-响应模型,无法满足 WebSocket 升级需返回 101 状态并长期维持连接的要求;必须绕过 Revel 路由,在 app.Init() 中用原生 http.HandleFunc 注册 handler,并在 Nginx 中透传 Upgrade 头。

Revel 框架本身不内置 WebSocket 支持,revel.WebSocket 并不存在,也不能像 gorilla/websocket 那样直接注册 handler。想在 Revel 项目里跑通在线聊天,必须绕过框架的 HTTP 路由层,手动接管底层 net.Conn,再用第三方库(如 gorilla/websocket)做协议升级和消息收发。
为什么 Revel 的 Controller 无法直接处理 WebSocket 升级请求
Revel 的 Controller 生命周期绑定在 HTTP 请求-响应模型上:一次请求进来,执行 Init → Begin → Action → Finish → 返回响应后连接即关闭。而 WebSocket 升级需要在响应头中写入 101 Switching Protocols,并保持 TCP 连接长期存活——这与 Revel 的请求生命周期天然冲突。
常见错误现象:
- 在
ChatController.Chat()里调用upgrader.Upgrade()后 panic 报错http: response.WriteHeader on hijacked connection - 前端
new WebSocket(...)一直卡在connecting状态,抓包发现服务端返回了 200 而非 101 - 即使强行 hijack 成功,后续消息读写会因 Revel 中间件(如
REVEL_RUNMODE=dev下的热重载、日志拦截器)干扰而中断
正确做法:在 Revel 启动时注册裸 HTTP handler
必须跳过 Revel 的路由系统,在 app.Init() 阶段,将 WebSocket 路径挂到 Go 原生 http.ServeMux 上,确保该路径完全绕过 Revel 中间件链。
实操建议:
- 在
app/init.go的init()函数末尾添加:http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { upgrader := websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true }, // 生产环境务必校验 Origin } conn, err := upgrader.Upgrade(w, r, nil) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } defer conn.Close() // 此处接入你的聊天逻辑,比如广播池、用户注册、心跳处理 handleChatConnection(conn) }) - 确保该 handler 在
revel.Server.Start()之前注册;否则 Revel 的http.ListenAndServe会覆盖掉你挂的路由 - 不要把
/ws写进routes文件——它不会生效,且可能引发 404 或 500
gorilla/websocket 连接管理需独立于 Revel 的 AppContext
Revel 的 revel.AppController 和 revel.Result 依赖当前 HTTP 请求上下文,而 WebSocket 连接生命周期远长于单次请求。所有用户状态、消息广播、房间管理必须自己维护,不能依赖 controller.RenderArgs 或 controller.Session。
关键注意事项:
- 每个
*websocket.Conn必须单独起 goroutine 调用conn.ReadMessage(),否则阻塞会导致整个连接假死 - 广播消息时避免直接在读消息 goroutine 里调用
conn.WriteMessage(),应通过 channel + select 实现读写分离,防止死锁 - Revel 的
revel.BEFORE/revel.AFTERfilter 对 WebSocket 连接完全不可见,认证必须在 upgrade 阶段完成(例如解析 query 参数中的 token,或校验 cookie) - 不要试图把
conn存进revel.Session——Session 是基于 HTTP Cookie 的短时效键值对,不支持持久连接句柄
生产部署时 Nginx 必须透传 Upgrade 头
如果用 Nginx 反向代理 Revel 应用(标准做法),默认会丢弃 Connection: upgrade 和 Upgrade: websocket 头,导致握手失败。
必须在 Nginx 配置中显式透传:
location /ws {
proxy_pass http://127.0.0.1:8089;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
漏掉 proxy_http_version 1.1 或 proxy_set_header Connection 中任意一项,前端都会收到 426 错误或直接断连。这个配置点极易被忽略,且错误表现不明确——浏览器控制台只显示 WebSocket connection to 'wss://...' failed,没有具体原因。











