Flask中request.files仅支持multipart/form-data上传,处理XML需先确认协议类型:multipart时用request.files.get('xml').seek(0)后解析;raw XML时用request.get_data()配合ET.fromstring(),并禁用DTD防XXE。
Flask 的 request.files 是专为 multipart/form-data 编码设计的,它依赖表单字段的 name 和文件头(如 Content-Disposition: form-data; name="file"; filename="data.xml")来提取文件。如果你用 fetch 或 curl 直接 POST 原始 XML 字符串(Content-Type: application/xml),request.files 会为空——这不是 Bug,是协议限制。
常见错误现象:request.files.get('xml') 返回 None,但 request.data 或 request.get_data() 却有内容。
curl -X POST -H "Content-Type: application/xml" --data-binary @data.xml http://localhost:5000/upload → 必须用 request.get_data()
+ enctype="multipart/form-data" → 才能用 request.files['xml']
FormData.append('xml', fileInput.files[0]) → 也走 multipart,可用 request.files
当确认是 multipart 提交后,request.files 返回的是 FileStorage 对象,不是普通文件句柄。不能直接用 xml.etree.ElementTree.parse() 传它,必须先定位到可读流位置(尤其多次读取时容易出错)。
from flask import Flask, request import xml.etree.ElementTree as ETapp = Flask(name)
@app.route('/upload', methods=['POST']) def upload_xml(): xml_file = request.files.get('xml') if not xml_file: return 'No file uploaded', 400
确保从开头读(避免因 prior read 导致空内容)
xml_file.seek(0) try: tree = ET.parse(xml_file) root = tree.getroot() return f'XML parsed: {root.tag}', 200 except ET.ParseError as e: return f'Invalid XML: {e}', 400
xml_file.seek(0) 很关键:某些中间件或日志记录可能已读过一次流,不重置会导致 ET.parse() 报 ParseError: no element found
xml_file.filename 做信任校验——客户端可伪造,只用于日志或调试xml_file.save('/path/to/save.xml'),但注意路径安全(过滤 ..、检查扩展名)生产环境常遇到前端发错类型(比如该发 multipart 却发了 raw XML),硬性拒绝不如优雅降级。可按优先级依次尝试:request.files → request.get_data(as_text=True) → request.get_data()(bytes)。
@app.route('/upload', methods=['POST'])
def upload_xml_flexible():
# 1. 尝试 multipart 文件
xml_file = request.files.get('xml')
if xml_file and xml_file.filename:
xml_file.seek(0)
content = xml_f
ile.read()
# 2. 否则尝试 raw body(application/xml 或 text/xml)
elif request.content_type in ['application/xml', 'text/xml']:
content = request.get_data()
else:
return 'Unsupported Content-Type or missing file', 400
try:
root = ET.fromstring(content)
return f'Parsed tag: {root.tag}', 200
except ET.ParseError as e:
return f'Malformed XML: {e}', 400request.content_type 是字符串,如 "application/xml; charset=utf-8",用 in 判断比 == 更鲁棒ET.fromstring() 接收 bytes 或 str,而 ET.parse() 只接受文件类对象或路径默认的 xml.etree.ElementTree 存在 XXE(XML External Entity)漏洞,如果用户上传恶意 XML 引用外部 DTD,可能读取服务器本地文件或发起内网请求。
XMLParser 显式关闭 load_dtd 和 resolve_entities
minidom 或 lxml 默认配置(它们更易开启 XXE)from xml.etree.ElementTree import XMLParser, parse from io import BytesIOparser = XMLParser() parser.parser.UseForeignDTD(False) # 禁用外部 DTD parser.feed(b'
') tree = parse(BytesIO(b' '), parser)
真正麻烦的不是怎么读 XML,而是你是否清楚这次请求到底走的是哪条路径——multipart?raw body?还是被反向代理篡改过 Content-Type?先抓包看清楚 request.headers 和 request.get_data(cache=True) 的原始字节,再决定用哪个分支。