3、消息接收与响应:接收普通文本消息、XML消息格式解析、被动回复用户消息
好,咱们进入实战环节。
前面两章我们把环境搭好了,也拿到了开发者权限。现在要干正事了——让我们的服务器能听懂用户说了什么,并且能回复。
说白了,微信服务器就是个「传话筒」。用户发一条消息给公众号,微信服务器把它转成一段XML,POST到你的服务器上。你的服务器解析完,再返回一段XML,微信服务器再转发给用户。整个过程,你想想看,是不是很像一个中间人?
3.1 接收普通文本消息
用户发来的消息,最常见的就是文本。比如用户说「你好」,微信服务器会发来这样一段XML:
<xml>
<ToUserName><![CDATA[gh_xxxxx]]></ToUserName>
<FromUserName><![CDATA[oXXXX_xxxx]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content>
<MsgId>1234567890123456</MsgId>
</xml>
嗯,这里要注意几个关键字段:
- ToUserName:开发者微信号(也就是你的公众号原始ID)
- FromUserName:用户的OpenID,每个用户唯一
- CreateTime:消息时间戳
- MsgType:消息类型,文本就是text
- Content:用户发的文本内容
- MsgId:消息ID,用于去重
我在项目中遇到过一个问题:用户连续发两条一模一样的消息,MsgId是不同的。所以千万别用Content去重,要用MsgId。
3.2 XML消息格式解析
收到XML后,第一件事就是解析。我个人习惯用Python的xml.etree.ElementTree,轻量又好用。当然你也可以用lxml,看团队技术栈。
解析的核心代码长这样:
import xml.etree.ElementTree as ET
def parse_xml(xml_data):
root = ET.fromstring(xml_data)
msg_dict = {}
for child in root:
msg_dict[child.tag] = child.text
return msg_dict
# 使用示例
xml_data = request.body.decode('utf-8')
msg = parse_xml(xml_data)
msg_type = msg.get('MsgType')
content = msg.get('Content')
这里有个坑,我踩过。CDATA段里的内容,ElementTree解析后会自动去掉CDATA标记,直接拿到纯文本。但如果你用某些框架的解析器,可能会保留CDATA标签。所以拿到数据后,最好打印出来看一眼。
3.3 被动回复用户消息
解析完消息,就该回复了。被动回复,说白了就是「用户发一条,你回一条」。你不能主动给用户发消息,只能响应。
回复的XML格式和接收的类似,只是ToUserName和FromUserName要互换:
<xml>
<ToUserName><![CDATA[oXXXX_xxxx]]></ToUserName>
<FromUserName><![CDATA[gh_xxxxx]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好,欢迎使用智能客服!]]></Content>
</xml>
回复文本消息的代码示例:
def build_text_response(to_user, from_user, content):
response = f"""<xml>
<ToUserName><![CDATA[{to_user}]]></ToUserName>
<FromUserName><![CDATA[{from_user}]]></FromUserName>
<CreateTime>{int(time.time())}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[{content}]]></Content>
</xml>"""
return response
# 在视图函数中
def wechat_handler(request):
if request.method == 'POST':
xml_data = request.body.decode('utf-8')
msg = parse_xml(xml_data)
to_user = msg.get('FromUserName')
from_user = msg.get('ToUserName')
content = msg.get('Content', '')
reply_content = f"你说了:{content}"
response_xml = build_text_response(to_user, from_user, reply_content)
return HttpResponse(response_xml, content_type='application/xml')
3.4 避坑指南
我曾经在线上环境遇到过一个诡异的问题:用户发消息,服务器明明返回了200,但用户收不到回复。查了半天,发现是返回的Content-Type写成了text/html,微信不认。必须写成application/xml。
还有一次,我同事把ToUserName和FromUserName写反了,结果用户收到了别人的回复。嗯,这种低级错误,调试时一定要仔细核对。
- 接收消息:解析XML,提取关键字段
- 回复消息:构造XML,注意字段互换
- 响应状态:必须返回200,Content-Type为application/xml
- 超时限制:微信服务器等待5秒,超时则重试3次
好了,这一章的内容就这些。说白了就是「收XML -> 解析 -> 构造XML -> 返回」。下一章我们会聊到图文消息和菜单事件的处理,到时候会更复杂一些,但核心逻辑是一样的。
公众号:蓝海资料掘金营,微信deep3321