You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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请求根本未到达后端。

解决方案

  1. 检查环境变量配置

    • 确认Vite环境变量文件(如.env.local或.env.production)中,VITE_WS_SERVER的值为ws://wasp-loved-alien.ngrok-free.app,注意不要添加端口号(ngrok已自动映射到后端3001端口)
    • 变量名必须以VITE_开头,否则Vite不会暴露给前端
  2. 验证环境变量读取
    在handleLogin函数中添加日志,确认变量值:

    console.log('WS Server:', import.meta.env.VITE_WS_SERVER);
    

    如果输出不是预期的ngrok地址,说明环境变量未正确加载。

  3. 重启Vite服务
    Vite在启动时加载环境变量,修改配置后需重启开发服务器才能生效。

  4. 生产构建注意事项
    如果是生产环境构建,需确保构建时环境变量正确注入(如通过CI/CD变量或构建命令传入)。

内容的提问来源于stack exchange,提问作者artistAhmed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 06:43:16