跳到主要内容

接收普通消息功能实现

消息处理概述

微信公众号的消息处理是开发的核心功能之一。当用户向公众号发送消息时,微信服务器会将消息转发到您的服务器,您需要解析消息内容并返回相应的回复。

消息处理流程

1. 消息交互流程

2. 消息类型

接收消息类型

  • 文本消息: 用户发送的文字
  • 图片消息: 用户发送的图片
  • 语音消息: 用户发送的语音
  • 视频消息: 用户发送的视频
  • 地理位置消息: 用户发送的位置信息
  • 链接消息: 用户发送的链接
  • 事件消息: 关注、取消关注、菜单点击等事件

回复消息类型

  • 文本回复: 回复文字消息
  • 图片回复: 回复图片
  • 语音回复: 回复语音
  • 视频回复: 回复视频
  • 音乐回复: 回复音乐
  • 图文消息: 回复图文组合

基础消息处理实现

1. 完整的服务器代码

const express = require('express');
const crypto = require('crypto');
const xml2js = require('xml2js');
const bodyParser = require('body-parser');

const app = express();
const PORT = 3000;
const TOKEN = 'your_token_here';

// 中间件配置
app.use(bodyParser.text({ type: 'application/xml' }));

// 验证签名函数
function verifySignature(signature, timestamp, nonce, token) {
const arr = [token, timestamp, nonce].sort();
const str = arr.join('');
const sha1 = crypto.createHash('sha1');
sha1.update(str);
return sha1.digest('hex') === signature;
}

// 处理微信验证请求
app.get('/wechat', (req, res) => {
const { signature, timestamp, nonce, echostr } = req.query;

if (verifySignature(signature, timestamp, nonce, TOKEN)) {
res.send(echostr);
} else {
res.status(403).send('Forbidden');
}
});

// 处理微信消息
app.post('/wechat', async (req, res) => {
try {
// 解析XML消息
const parser = new xml2js.Parser();
const result = await parser.parseStringPromise(req.body);
const message = result.xml;

// 获取消息基本信息
const toUserName = message.ToUserName[0];
const fromUserName = message.FromUserName[0];
const createTime = message.CreateTime[0];
const msgType = message.MsgType[0];

console.log('收到消息:', {
from: fromUserName,
type: msgType,
content: message.Content ? message.Content[0] : 'N/A'
});

// 根据消息类型处理
let reply = '';
switch (msgType) {
case 'text':
reply = handleTextMessage(message, fromUserName, toUserName);
break;
case 'image':
reply = handleImageMessage(message, fromUserName, toUserName);
break;
case 'voice':
reply = handleVoiceMessage(message, fromUserName, toUserName);
break;
case 'video':
reply = handleVideoMessage(message, fromUserName, toUserName);
break;
case 'location':
reply = handleLocationMessage(message, fromUserName, toUserName);
break;
case 'link':
reply = handleLinkMessage(message, fromUserName, toUserName);
break;
case 'event':
reply = handleEventMessage(message, fromUserName, toUserName);
break;
default:
reply = createTextReply(fromUserName, toUserName, '暂不支持此类型消息');
}

res.type('application/xml');
res.send(reply);
} catch (error) {
console.error('处理消息错误:', error);
res.status(500).send('Internal Server Error');
}
});

// 处理文本消息
function handleTextMessage(message, fromUserName, toUserName) {
const content = message.Content[0];

// 简单的关键词回复
if (content.includes('你好') || content.includes('hello')) {
return createTextReply(fromUserName, toUserName, '你好!欢迎关注我们的公众号!');
} else if (content.includes('帮助') || content.includes('help')) {
return createTextReply(fromUserName, toUserName, '您可以发送以下关键词:\n- 你好\n- 帮助\n- 天气\n- 时间');
} else if (content.includes('天气')) {
return createTextReply(fromUserName, toUserName, '抱歉,天气功能正在开发中...');
} else if (content.includes('时间')) {
const now = new Date();
return createTextReply(fromUserName, toUserName, `当前时间:${now.toLocaleString()}`);
} else {
return createTextReply(fromUserName, toUserName, `您发送的是:${content}\n\n如需帮助,请发送"帮助"`);
}
}

// 处理图片消息
function handleImageMessage(message, fromUserName, toUserName) {
const picUrl = message.PicUrl[0];
const msgId = message.MsgId[0];

return createTextReply(fromUserName, toUserName,
`收到您的图片!\n图片链接:${picUrl}\n消息ID:${msgId}`);
}

// 处理语音消息
function handleVoiceMessage(message, fromUserName, toUserName) {
const format = message.Format[0];
const msgId = message.MsgId[0];

return createTextReply(fromUserName, toUserName,
`收到您的语音消息!\n格式:${format}\n消息ID:${msgId}`);
}

// 处理视频消息
function handleVideoMessage(message, fromUserName, toUserName) {
const thumbMediaId = message.ThumbMediaId[0];
const msgId = message.MsgId[0];

return createTextReply(fromUserName, toUserName,
`收到您的视频消息!\n缩略图ID:${thumbMediaId}\n消息ID:${msgId}`);
}

// 处理地理位置消息
function handleLocationMessage(message, fromUserName, toUserName) {
const locationX = message.Location_X[0];
const locationY = message.Location_Y[0];
const scale = message.Scale[0];
const label = message.Label[0];

return createTextReply(fromUserName, toUserName,
`收到您的位置信息!\n位置:${label}\n经度:${locationX}\n纬度:${locationY}\n缩放:${scale}`);
}

// 处理链接消息
function handleLinkMessage(message, fromUserName, toUserName) {
const title = message.Title[0];
const description = message.Description[0];
const url = message.Url[0];

return createTextReply(fromUserName, toUserName,
`收到您分享的链接!\n标题:${title}\n描述:${description}\n链接:${url}`);
}

// 处理事件消息
function handleEventMessage(message, fromUserName, toUserName) {
const event = message.Event[0];

switch (event) {
case 'subscribe':
return createTextReply(fromUserName, toUserName,
'感谢关注我们的公众号!\n\n您可以发送以下关键词:\n- 你好\n- 帮助\n- 天气\n- 时间');
case 'unsubscribe':
// 用户取消关注,不需要回复
return '';
case 'CLICK':
const eventKey = message.EventKey[0];
return handleMenuClick(eventKey, fromUserName, toUserName);
default:
return createTextReply(fromUserName, toUserName, `收到事件:${event}`);
}
}

// 处理菜单点击事件
function handleMenuClick(eventKey, fromUserName, toUserName) {
switch (eventKey) {
case 'MENU_ABOUT':
return createTextReply(fromUserName, toUserName, '关于我们:这是一个微信公众号开发教程示例');
case 'MENU_CONTACT':
return createTextReply(fromUserName, toUserName, '联系我们:请发送邮件至 example@example.com');
default:
return createTextReply(fromUserName, toUserName, `点击了菜单:${eventKey}`);
}
}

// 创建文本回复
function createTextReply(fromUserName, toUserName, content) {
return `
<xml>
<ToUserName><![CDATA[${fromUserName}]]></ToUserName>
<FromUserName><![CDATA[${toUserName}]]></FromUserName>
<CreateTime>${Date.now()}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[${content}]]></Content>
</xml>
`;
}

// 启动服务器
app.listen(PORT, () => {
console.log(`服务器运行在端口 ${PORT}`);
console.log(`微信验证地址: http://localhost:${PORT}/wechat`);
});

2. 依赖安装

# 安装必要的依赖
npm init -y
npm install express crypto xml2js body-parser

测试和调试

1. 本地测试

# 启动服务器
node app.js

# 使用ngrok进行外网映射
ngrok http 3000

# 配置微信公众平台
# URL: https://your-ngrok-url.ngrok.io/wechat

2. 常见问题

问题:消息接收失败

原因:

  • 服务器地址配置错误
  • 签名验证失败
  • 服务器响应超时

解决方案:

  • 检查服务器地址配置
  • 验证签名算法
  • 确保服务器响应时间在5秒内

问题:XML解析错误

原因:

  • XML格式不正确
  • 编码问题
  • 解析库版本问题

解决方案:

  • 检查XML格式
  • 设置正确的编码
  • 更新xml2js库版本

最佳实践

1. 性能优化

  • 异步处理: 使用异步函数处理消息
  • 缓存机制: 缓存access_token等数据
  • 连接池: 使用数据库连接池

2. 安全考虑

  • 输入验证: 验证所有输入数据
  • 签名验证: 严格验证微信签名
  • 错误处理: 避免泄露敏感信息

3. 代码组织

  • 模块化设计: 将功能分离到不同模块
  • 配置管理: 使用环境变量管理配置
  • 日志记录: 完善的日志记录系统

总结

消息处理是微信公众号开发的核心功能。通过正确实现消息接收和处理逻辑,您可以构建功能丰富的微信公众号应用。

在接下来的学习中,我们将介绍如何实现自动回复文本功能,以及如何处理多媒体消息。