苍穹外卖WebSocket连接问题
在调试过程中发现,当前端应用部署在Nginx下访问localhost时,WebSocket连接无法建立;而直接运行前端项目时则可以正常连接。通过浏览器F12排查问题后,发现是WebSocket的URL路径不同:我们实际需要的地址为ws://localhost/ws/,但Nginx转发的请求却指向了ws://localhost/,缺少了关键的/ws/路径前缀。为解决该问题,在Nginx配置文件中添加了相应的路径重写规则,于是在nginx.conf中配置以下代码:
# WebSocket location /ws/ { #proxy_pass http://localhost:8080/admin/; proxy_pass http://webservers/ws/; proxy_http_version 1.1; proxy_read_timeout 3600s; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "$connection_upgrade"; } # WebSocket 没有 /ws 前缀 location ~ ^/([a-zA-Z0-9_-]{8,32})$ { proxy_pass http://webservers/ws/$1; proxy_http_version 1.1; proxy_read_timeout 3600s; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "$connection_upgrade"; }调整配置后,Nginx能够正确地连接上WebSocket,但是请求却不是转发至/ws/路径,而是相当于重新转发至ws://localhost/,问题并没有被彻底解决。同时又发现新的问题:WebSocket连接成功建立后,当有订单到达时,有消息弹窗但是没有声音。在浏览器F12控制台中,发现了以下错误:
“Uncaught (in promise) NotAllowedError: play() failed because the user didn't interact with the document first.”
该错误源于现代浏览器的自动播放安全策略。浏览器要求用户必须先与页面进行交互(如点击、触摸等),才能允许音频或视频自动播放,以防止网站在用户未授权的情况下擅自播放媒体内容。
这个问题解决方案倒是相对简单:只需在页面上进行一次用户交互(例如点击任意区域),之后再触发消息弹窗时,音频即可正常播放。
回到websocket连接问题,在之前的Nginx配置中,使用正则表达式location ~ ^/([a-zA-Z0-9_-]{8,32})$可能会与其他路径产生冲突,因此需要从根源上解决问题。让我们到前端代码进行排查。前端项目路径为project-sky-admin-vue-ts\src\layout\components\Navbar\index.vue,其中webSocket方法的代码如下:
webSocket() { const that = this as any let clientId = Math.random().toString(36).substr(2) //let socketUrl = process.env.VUE_APP_SOCKET_URL + clientId // 使用硬编码的WebSocket URL let socketUrl = 'ws://localhost:8080/ws/' + clientId console.log(socketUrl, 'socketUrl') if (typeof WebSocket == 'undefined') { that.$notify({ title: '提示', message: '当前浏览器无法接收实时报警信息,请使用谷歌浏览器!', type: 'warning', duration: 0, }) } // ... 后续代码 }通过测试发现,将 URL 硬编码为'ws://localhost:8080/ws/' + clientId后 WebSocket 能够正常连接,这表明问题出在process.env.VUE_APP_SOCKET_URL环境变量的取值上。尽管尝试了多种方法查找资料,依旧未能彻底解决环境变量配置不正确的问题。但至少目前功能可用。鉴于该问题依然存在,那就得请教一下各位了。
