flask需用request.get_data()获取微信xml原始数据,因默认解析器不支持text/xml;须用defusedxml安全解析防xxe攻击;提取字段用root.findtext()避免异常;本地调试需公网https且注意幂等处理。

Flask怎么拿到微信发来的XML原始数据
微信公众号事件推送(如关注、点击菜单、扫码等)是用 POST 请求发送 XML 数据,但 Flask 默认的 request.form 和 request.json 都解析不了它——前者只认 application/x-www-form-urlencoded,后者只认 application/json,而微信发的是 text/xml 或 application/xml。
必须手动读取原始请求体:
-
request.get_data()是最直接的方式,返回bytes,需解码为字符串再解析 - 不能用
request.data之后又调request.form—— Flask 的底层会把 body 读一次就清空,重复读会得到空值 - 微信服务器要求 5 秒内响应,所以解析要快,别在路由里做耗时 XML 校验或网络请求
示例:
from flask import Flask, request
app = Flask(__name__)
<p>@app.route('/wechat', methods=['POST'])
def wechat():
xml_data = request.get_data() # ← 关键:拿原始 bytes
if not xml_data:
return 'fail', 400
xml_str = xml_data.decode('utf-8') # 微信固定用 UTF-8</p><h1>后续用 xml.etree.ElementTree 或 defusedxml 解析</h1>
用什么库安全解析微信 XML,避免 XXE 攻击
微信 XML 是外部不可信输入,直接用 Python 内置的 xml.etree.ElementTree.fromstring() 有 XXE(XML External Entity)风险,攻击者可能构造恶意 XML 读取服务器本地文件或发起内网请求。
必须换更安全的替代方案:
由于微信的大热,为了更好的方便使用微信的用户查询一些信息,这篇文章是入门级的微信公众平台开发教程,需要的朋友可以参考下 这篇入门教程将引导你完成如下任务: 创建百度云平台应用启用微信公众平台开发模式获取订阅、文字、图片、语音、视频消息回复文本、图文及音乐消息程序开发
- 优先用
defusedxml.ElementTree.fromstring(),它是专为防御 XXE 设计的xml.etree.ElementTree替代品 - 不要用
minidom或lxml(除非显式禁用外部实体),它们默认开启 DTD 解析 - 如果坚持用标准库,得手动配置 parser:
xml.etree.ElementTree.XMLParser(resolve_entities=False),但不如defusedxml省心
安装与用法:
pip install defusedxml # …… from defusedxml.ElementTree import fromstring root = fromstring(xml_str) # 安全解析
怎么提取微信 XML 里的 ToUserName、Event 这些字段
微信事件 XML 结构固定,顶层是 <xml></xml>,子节点全是直系文本节点(没有嵌套层级),比如 <tousername>gh_xxx</tousername>。直接用 .findtext() 最简单可靠。
-
root.findtext('ToUserName')返回字符串,找不到返回None,适合判空 - 别用
root.find('ToUserName').text—— 如果节点不存在,.find()返回None,再点.text就抛AttributeError - 注意大小写:微信字段名严格区分大小写,
EventKey不是eventkey,MsgType不是msgtype - 文本内容可能含 CDATA,
defusedxml会自动解包,不用额外处理
常用字段提取示例:
to_user = root.findtext('ToUserName')
from_user = root.findtext('FromUserName')
msg_type = root.findtext('MsgType') # 'event' 或 'text'
event = root.findtext('Event') # 'subscribe' / 'CLICK' / 'SCAN'
event_key = root.findtext('EventKey') # 菜单 key 或扫码参数
为什么本地调试收不到微信 XML,或者解析出空值
本地开发时常见现象:微信后台提示“请求 URL 超时”或“配置失败”,或者 Flask 日志里 request.get_data() 返回空 bytes。根本原因不是代码错,而是环境链路问题。
- 微信服务器只支持公网可访问的 HTTPS URL,
http://localhost:5000或内网 IP 必然失败,必须用ngrok、localtunnel或真实域名 + SSL 证书 - Flask 默认不处理
Content-Type: text/xml的请求头,但不影响request.get_data()读 body;真正影响的是反向代理(如 Nginx)可能截断或转义 XML —— 检查代理配置是否加了proxy_set_header Content-Type text/xml; - 微信会在首次配置 URL 时 GET 请求校验 token,但事件推送一定是 POST;别把两个逻辑混在一起处理,否则 POST 时因没匹配到路由返回 405
- 微信有时会重试失败推送,导致同一事件收到多次,业务层要做幂等(比如用
MsgId去重)
调试建议:用微信开发者工具「接口调试」功能,手动 POST XML 到你的地址,比等真实事件更快定位问题。










