Node Express结合whatsapp-web-js无法发送消息问题排查
问题:WhatsApp消息发送路由无限加载无报错
我正在搭建一个React前端+自定义API服务器的WhatsApp消息发送系统,目前获取二维码、处理认证的路由都正常,但发送指定号码消息的POST路由无法工作。用Postman请求该端点时,会无限加载且没有任何报错信息,以下是服务器代码,请求排查问题:
const express = require('express') const router = express.Router() const fs = require('fs') const { Client, LocalAuth } = require('whatsapp-web.js') const authStrategy = new LocalAuth({ clientId: 'adminSession', }) const worker = `${authStrategy.dataPath}/session-admin/Default/Service Worker` if (fs.existsSync(worker)) { fs.rmdirSync(worker, { recursive: true }) } const client = new Client({ takeoverOnConflict: true, authStrategy, }) const sessionData = { client: 'admin', session: true, qrCodeScanned: true, } client.on('authenticated', (session) => { fs.writeFile( 'waSession.json', JSON.stringify(sessionData), 'utf-8', (err) => { if (!err) { console.log('Session saved on disk...') } } ) }) router.get('/whatsapp/auth', async (req, res) => { const dir = './waSession.json' fs.readFile(dir, (err, data) => { if (data.length === 0) { return res.status(200).json({ message: 'You need to login first', }) } else { return res.status(200).json({ message: 'You are logged in.', }) } }) }) router.get('/whatsapp', async (req, res) => { try { client.on('qr', (qr) => { res.status(200).send({ message: 'Connect whatsapp with this qr-code', qrCode: qr, }) }) await client.initialize() res.status(404) } catch (err) { res.send(err) } }) router.post('/whatsapp/send', async (req, res) => { const { phoneNumber, message } = req.body try { client.on('ready', async () => { const number = phoneNumber const text = message const chatId = number.substring(1) + '@c.us' await client.sendMessage(chatId, text) }) await client.initialize() res.json('Messaggio inviato') } catch (err) { res.status(404).send(err) await client.destroy() } }) module.exports = router
核心问题分析
- 重复初始化+事件绑定逻辑错误
/whatsapp/send路由每次被请求时,都会给全局client绑定新的ready事件,还重复调用client.initialize()。但whatsapp-web.js的Client实例是全局单例,重复初始化会导致状态混乱;且ready事件仅在客户端首次就绪时触发一次,后续请求绑定的事件永远不会执行。
- 响应时机完全错误
- 你在
ready事件内部执行发消息,但在client.initialize()后立刻返回了响应,此时消息根本没发送。如果ready事件触发时响应已经发送,还会导致无法正确反馈发送结果。
- 你在
- 错误处理缺失
- 发送消息过程中没有捕获号码格式错误、客户端未就绪等异常,且错误分支里的
client.destroy()会直接销毁全局客户端,导致后续所有功能失效。
- 发送消息过程中没有捕获号码格式错误、客户端未就绪等异常,且错误分支里的
修复后的代码示例
const express = require('express') const router = express.Router() const fs = require('fs') const { Client, LocalAuth } = require('whatsapp-web.js') const authStrategy = new LocalAuth({ clientId: 'adminSession', }) const worker = `${authStrategy.dataPath}/session-admin/Default/Service Worker` if (fs.existsSync(worker)) { fs.rmdirSync(worker, { recursive: true }) } // 全局初始化Client,仅执行一次 const client = new Client({ takeoverOnConflict: true, authStrategy, }) const sessionData = { client: 'admin', session: true, qrCodeScanned: true, } client.on('authenticated', (session) => { fs.writeFile( 'waSession.json', JSON.stringify(sessionData), 'utf-8', (err) => { if (!err) { console.log('Session saved on disk...') } } ) }) // 全局初始化客户端,不要在路由内重复调用 client.initialize().catch(err => console.error('客户端初始化失败:', err)) router.get('/whatsapp/auth', async (req, res) => { const dir = './waSession.json' fs.readFile(dir, (err, data) => { // 增加文件不存在的错误判断 if (err || data.length === 0) { return res.status(200).json({ message: 'You need to login first', }) } else { return res.status(200).json({ message: 'You are logged in.', }) } }) }) router.get('/whatsapp', async (req, res) => { try { // 绑定QR事件后,发送响应立即移除监听,避免重复触发 const qrListener = (qr) => { res.status(200).send({ message: 'Connect whatsapp with this qr-code', qrCode: qr, }) client.off('qr', qrListener) } client.on('qr', qrListener) // 如果客户端已经就绪,直接返回状态 if (client.info) { res.status(200).json({ message: '客户端已就绪' }) client.off('qr', qrListener) } } catch (err) { res.status(500).send(err) } }) router.post('/whatsapp/send', async (req, res) => { const { phoneNumber, message } = req.body try { // 先检查客户端是否就绪 if (!client.info) { return res.status(400).json({ message: '客户端未就绪,请先扫码登录' }) } // 验证号码格式(需为国际区号开头,如+123456789) if (!phoneNumber || !phoneNumber.match(/^\+\d+$/)) { return res.status(400).json({ message: '请输入有效的国际格式号码' }) } const chatId = phoneNumber.substring(1) + '@c.us' await client.sendMessage(chatId, message) res.json({ message: '消息发送成功' }) } catch (err) { console.error('发送消息失败:', err) res.status(500).json({ message: '消息发送失败', error: err.message }) } }) module.exports = router
关键修复点
- 全局初始化Client:将
client.initialize()移到路由外部,仅执行一次,避免重复初始化导致的状态混乱。 - 移除路由内的事件绑定:不再在
/send路由里绑定ready事件,直接检查客户端状态,就绪则立即发送消息。 - 修正响应时机:只有在消息成功发送后才返回响应,失败则返回具体错误信息。
- 增加参数验证:提前检查号码格式和客户端状态,避免无效操作。
- 优化QR事件处理:发送QR码后立即移除监听,避免后续请求重复触发。
内容的提问来源于stack exchange,提问作者MarkWriteCodes
相关产品推荐
相关产品推荐

