Beego中WebSocket连接403或立即关闭,主因是默认Origin校验拦截;需配置websocket.alloworigin = *(开发)或具体域名(生产),并用beego.WebSocket()注册路由,而非Router()。

Beego 中 WebSocket 连接被拒绝的常见原因
直接返回 403 或连接立即关闭,大概率不是代码逻辑问题,而是 Beego 的默认安全策略在拦截。Beego 1.12+ 版本默认启用 EnableDocs 和 EnableAdmin 外,还对非同源 WebSocket 升级请求(Upgrade: websocket)做 Origin 校验。
实操建议:
- 确认请求头中是否携带
Origin;若前端用new WebSocket("ws://localhost:8080/ws")发起,浏览器会自动带上Origin: http://localhost:8080,但后端未配置白名单时会被拒绝 - 在
app.conf中显式关闭 Origin 检查:websocket.alloworigin = *(开发环境可用,生产环境应设为具体域名) - 确保路由注册方式正确:不能用
beego.Router("/ws", &Wshandler{}),必须用beego.WebSocket("/ws", &Wshandler{}),否则框架不会注入 WebSocket 协议支持中间件
WebSocket 控制器里如何读写消息而不 panic
Beego 的 WebSocket 控制器继承自 beego.Controller,但实际通信对象是 this.Ctx.Input.Conn——它是一个 net.Conn,**不是**标准的 http.ResponseWriter。直接调用 this.Data["json"] = ... 或 this.ServeJSON() 会 panic。
实操建议:
- 读取消息用
conn.ReadMessage()(注意:Beego 封装的Conn支持ReadJSON/WriteJSON,但底层依赖gorilla/websocket) - 发送消息统一走
conn.WriteJSON(v interface{}),不要用this.TplName或模板渲染 - 务必检查
conn是否为nil:Beego 在 WebSocket 升级成功后才设置this.Ctx.Input.Conn,如果在Prepare()阶段就访问会 panic - 连接关闭时,
conn不会自动置nil,需自行判断conn.RemoteAddr() == nil或捕获io.EOF
如何让多个客户端之间广播消息
Beego 本身不提供全局连接池或房间管理,需要自己维护在线连接列表。关键点在于并发安全和连接生命周期同步。
实操建议:
- 用
sync.Map存储map[string]*websocket.Conn,key 可以是用户 ID 或随机 sessionID(从this.Ctx.Input.Session.Get("uid")获取,前提是已登录) - 在
Finish()方法里主动删除连接:connMap.Delete(sessionID),而不是只依赖defer—— 因为 Beego 的Finish()保证在连接关闭后执行,比defer更可靠 - 广播时遍历
sync.Map,对每个conn调用WriteJSON(),但要加超时控制(如conn.SetWriteDeadline(time.Now().Add(5 * time.Second))),避免一个卡死拖垮全部 - 不要在广播循环里直接
delete失败连接,先收集失效 key,再统一清理,否则sync.Map.Range期间修改会引发不可预测行为
生产环境部署时 Nginx 透传 WebSocket 的配置要点
本地调试通了,一上 Nginx 就报 Error during WebSocket handshake: Unexpected response code: 400,基本是反向代理没透传升级头。
实操建议:
- Nginx 配置中必须显式设置:
proxy_http_version 1.1;、proxy_set_header Upgrade $http_upgrade;、proxy_set_header Connection "upgrade"; - Beego 应用监听地址不能是
127.0.0.1:8080,而要用0.0.0.0:8080,否则 Nginx 无法反向代理到容器/本机其他网卡 - 如果用了 HTTPS,前端
WebSocket地址必须是wss://,且 Nginx 的 SSL 配置需完整(含证书链),否则浏览器会静默失败 - Beego 日志里看不到 WebSocket 升级日志?打开
AccessLogs = true并确认LogLevel≥ 3(LoggerLevelInfo),升级请求会被记录为GET /ws HTTP/1.1 101
真正麻烦的从来不是连上 WebSocket,而是连接断开时的重试策略、消息丢失补偿、用户状态同步这些边缘 case。Beego 给你的是一个干净的 net.Conn,剩下的得你自己拿锁、选序列化格式、决定要不要加心跳包。


















