Vue+Express应用中WebSocket连接失败问题求助
Express + ngrok WebSocket连接失败问题排查
问题概述
开发基于Express的后端应用,通过ngrok部署后,POST请求可正常返回预期结果,但WebSocket连接无法建立。环境变量中配置的WebSocket地址为ws://wasp-loved-alien.ngrok-free.app,但浏览器控制台显示前端尝试连接ws://localhost:3001/awaitlogin/[challengeID]并失败。
浏览器控制台错误日志
Login Response: {data: {…}, success: true, status: 200} QrLogin.vue:57 WebSocket connection to 'ws://localhost:3001/awaitlogin/i7na5HZFuEL2NKb6MYmGLC3DoxEnKpi7jL' failed: (anonymous) @ QrLogin.vue:57 callWithErrorHandling @ vue.js:2297 callWithAsyncErrorHandling @ vue.js:2304 baseWatchOptions.call @ vue.js:8270 job @ vue.js:2026 callWithErrorHandling @ vue.js:2297 flushJobs @ vue.js:2505 Show 6 more frames Show less QrLogin.vue:72 WebSocket Error: Event {isTrusted: true, type: 'error', target: WebSocket, currentTarget: WebSocket, eventPhase: 2, …} socket.onerror @ QrLogin.vue:72 error (async) (anonymous) @ QrLogin.vue:71 callWithErrorHandling @ vue.js:2297 callWithAsyncErrorHandling @ vue.js:2304 baseWatchOptions.call @ vue.js:8270 job @ vue.js:2026 callWithErrorHandling @ vue.js:2297 flushJobs @ vue.js:2505 Promise.then (async) queueFlush @ vue.js:2419 queueJob @ vue.js:2414 baseWatchOptions.scheduler @ vue.js:8282 effect2.scheduler @ vue.js:2043 trigger @ vue.js:544 endBatch @ vue.js:602 notify @ vue.js:862 trigger @ vue.js:836 set value @ vue.js:1707 handleLogin @ QrLogin.vue:42 await in handleLogin (async) callWithErrorHandling @ vue.js:2297 callWithAsyncErrorHandling @ vue.js:2304 invoker @ vue.js:11190 Show 18 more frames Show less QrLogin.vue:76 ~
前端组件代码(QrLogin.vue)
<template> <div> <h3>Sign In</h3> <div v-if="loading"> <Loading /> </div> <div v-else> <div v-if="QRData"> <QRCode :value="QRData" /> <p>Please scan the QR code with your Verus Mobile app.</p> </div> <div v-else> <button @click="handleLogin">Login with your ID</button> </div> <p> Get <a href="#">Verus Wallet</a> </p> </div> </div> </template> <script setup> import { ref, watch } from 'vue'; import { login } from '../scripts/login'; import Loading from './Loading.vue'; import QRCode from 'qrcode-vue' const challengeID = ref(null); const showLoginQR = ref(false); const QRData = ref(''); const loading = ref(false); const handleLogin = async () => { try { loading.value = true; const reply = await login(); console.log('Login Response:', reply); if (reply?.success) { showLoginQR.value = true; QRData.value = reply.data.deepLink; challengeID.value = reply.data.challengeID; // This will be set when login is successful loading.value = false; } else { // Handle error here loading.value = false; } } catch (error) { console.error(error); loading.value = false; } }; // Watch for changes in challengeID and initiate WebSocket connection when it is set watch(challengeID, (newChallengeID) => { if (newChallengeID) { const socket = new WebSocket(`${import.meta.env.VITE_WS_SERVER}/awaitlogin/${newChallengeID}`); socket.onopen = () => { console.log("WebSocket connection established"); }; socket.onmessage = (event) => { const receivedMessage = JSON.parse(event.data); console.log("Received message:", receivedMessage); localStorage.setItem("token", JSON.stringify(receivedMessage.JWT)); localStorage.setItem("iaddress", receivedMessage.iaddress); localStorage.setItem("name", receivedMessage.name); window.location.assign("/loggedin"); }; socket.onerror = (error) => { console.error("WebSocket Error:", error); }; socket.onclose = () => { console.log("WebSocket connection closed"); }; return () => { socket.close(); }; } }); </script>
后端WebSocket处理代码(websocket.js)
import expressWs from 'express-ws'; import cors from 'cors'; const app = express(); expressWs(app); const registeredChallengeIDs = new Set(); const clients = new Map(); app.ws('/awaitlogin/:challengeid', (ws, req) => { const { challengeid } = req.params; console.log(`WebSocket challengeid connected ${challengeid}`); clients.set(ws, challengeid); ws.on('message', (data) => { console.log(`Received message: ${data}`); }); ws.on('close', () => { clients.delete(ws); console.log('WebSocket client disconnected'); }); }); export { registeredChallengeIDs, clients, app };
问题分析与解决方案
核心问题
从错误日志可见,前端实际尝试连接的是localhost:3001而非配置的ngrok地址,说明环境变量VITE_WS_SERVER未正确加载或配置错误。后端无日志输出也验证了WebSocket请求根本未到达后端。
解决方案
检查环境变量配置
- 确认Vite环境变量文件(如
.env.local或.env.production)中,VITE_WS_SERVER的值为ws://wasp-loved-alien.ngrok-free.app,注意不要添加端口号(ngrok已自动映射到后端3001端口) - 变量名必须以
VITE_开头,否则Vite不会暴露给前端
- 确认Vite环境变量文件(如
验证环境变量读取
在handleLogin函数中添加日志,确认变量值:console.log('WS Server:', import.meta.env.VITE_WS_SERVER);如果输出不是预期的ngrok地址,说明环境变量未正确加载。
重启Vite服务
Vite在启动时加载环境变量,修改配置后需重启开发服务器才能生效。生产构建注意事项
如果是生产环境构建,需确保构建时环境变量正确注入(如通过CI/CD变量或构建命令传入)。
内容的提问来源于stack exchange,提问作者artistAhmed
相关产品推荐
相关产品推荐

