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

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

核心问题分析

  1. 重复初始化+事件绑定逻辑错误
    • /whatsapp/send路由每次被请求时,都会给全局client绑定新的ready事件,还重复调用client.initialize()。但whatsapp-web.js的Client实例是全局单例,重复初始化会导致状态混乱;且ready事件仅在客户端首次就绪时触发一次,后续请求绑定的事件永远不会执行。
  2. 响应时机完全错误
    • 你在ready事件内部执行发消息,但在client.initialize()后立刻返回了响应,此时消息根本没发送。如果ready事件触发时响应已经发送,还会导致无法正确反馈发送结果。
  3. 错误处理缺失
    • 发送消息过程中没有捕获号码格式错误、客户端未就绪等异常,且错误分支里的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 19:33:27