微信公众号事件推送xml必须用request.get_data()读原始body,因request.form会消耗请求体导致request.data为空;需用defusedxml防xxe,字段用findtext()安全提取,且须https响应5秒内完成。

微信公众号事件推送的 XML 数据不能用 request.form 或 request.json 拿到,必须用 request.get_data() 读原始 body,否则会为空或解析失败。
为什么 request.data 和 request.get_data() 都行但推荐后者
request.data 是 Flask 的属性,返回已读取一次的原始 bytes;request.get_data() 是方法,可显式控制是否缓存、是否强制读取。两者在首次调用时效果一致,但一旦你误触过 request.form 或 request.json,request.data 就会变空——因为 Flask 底层把请求体消费掉了。
- 用
request.get_data(cache=True)更稳妥,它会自动缓存并允许多次调用 - 别在同一个请求里混用
request.data和request.form,哪怕只读一次request.form,后续再取request.data就是空 bytes - 调试时打印
len(request.get_data())能快速确认有没有拿到数据
用 defusedxml 解析 XML 防 XXE 攻击
微信发来的 XML 是外部不可信输入,Python 标准库 xml.etree.ElementTree.fromstring() 默认开启 DTD 解析,攻击者可构造恶意实体读取服务器文件或打内网。直接禁用 DTD 不够可靠,应换专用安全库。
Python 3.14.2是Python编程语言在2025年12月5日发布的稳定版本,属于3.14系列的第二个维护更新。该版本包含了18项修复,重点解决了多进程、数据类及正则表达式等模块的回归问题,并修复了CVE-2025-12084等安全漏洞。此版本标志着自由线程模式(移除GIL)正式获得官方支持,是Python发展的重要里程碑。
- 安装:
pip install defusedxml - 导入写法必须是:
from defusedxml.ElementTree import fromstring,不是import defusedxml.ElementTree as ET - 不要用
lxml或minidom,除非你手动设置resolve_entities=False和load_dtd=False,但仍有遗漏风险 - 示例:
root = fromstring(xml_str.encode('utf-8')),注意传 bytes 更稳,避免编码歧义
提取字段用 findtext(),别用 find().text
微信 XML 所有字段都是顶层直系子节点(如 <tousername></tousername>),没有嵌套层级,但部分字段可能带 包裹。用 findtext() 自动处理 CDATA,且返回 None 而非抛异常,适合判空逻辑。
- 正确:
root.findtext('ToUserName')、root.findtext('MsgType', 'text')(第二个参数设默认值) - 危险:
root.find('ToUserName').text—— 如果节点不存在,find()返回None,再点.text就报AttributeError - 注意大小写:微信字段名全小写,如
fromusername是错的,必须是FromUserName - 时间戳字段
CreateTime是整型字符串,用int(root.findtext('CreateTime', '0'))转换更安全
本地调试必须走公网 HTTPS,且响应要快
微信服务器只向 HTTPS 地址发起回调,且要求 5 秒内返回 HTTP 200 响应体(内容任意,但不能超时)。本地 http://localhost 直接无效,必须穿透或部署测试环境。
- 推荐用
ngrok http 5000或localtunnel获取临时 HTTPS 域名 - Flask 路由里别做耗时操作:不查数据库、不调第三方 API、不写大日志;校验签名、解析 XML、拼回复 XML 这三步必须在毫秒级完成
- 幂等性要自己加:微信可能重试推送(比如你响应超时),需用
MsgId去重,不能每来一次就发一遍消息 - 测试时先用 curl 模拟 POST,确认 XML 解析逻辑跑通,再对接微信后台验证 URL
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










