Actix-web无法直接用web::Json处理XML请求,因其硬编码只认application/json;需手动读取body字节并用serde_xml_rs::from_reader解析,注意同步解析、字段重命名及Content-Type校验。

Actix-web 接收 XML 请求体时为何不能直接用 web::Json?
因为 web::Json 内部硬编码依赖 serde_json,只认 Content-Type: application/json,且会自动拒绝非 JSON MIME 类型。XML 请求(application/xml 或 text/xml)会被直接拦截并返回 400 错误 —— 这不是配置问题,是类型守门员的强制策略。
必须绕过默认 extractor,手动读取原始 body 并用 serde_xml_rs 解析。
如何用 serde_xml_rs 解析 Actix-web 的 XML body?
核心步骤:禁用自动解析 → 读取完整字节流 → 用 serde_xml_rs::from_reader 解析。注意两点:一是 serde_xml_rs 不支持异步 reader,所以必须先 .await 收完全部 body;二是它默认不处理 XML 声明(如 ),但实际能容忍,无需提前 strip。
- 确保 struct 字段加
#[serde(rename = "xxx")]匹配 XML tag 名(大小写、中划线敏感) - 嵌套元素用结构体字段,文本内容用
#[serde(rename = "$value")] - 可选字段用
Option,缺失标签会被反序列化为None - 数组用
Vec,前提是 XML 中有多个同名 tag(serde_xml_rs自动聚合)
use serde::Deserialize;
use actix_web::{web, HttpResponse, HttpRequest, HttpError};
use serde_xml_rs;
#[derive(Deserialize, Debug)]
struct User {
#[serde(rename = "user_id")]
id: u32,
#[serde(rename = "full-name")]
full_name: String,
#[serde(rename = "$value")]
content: Option,
}
async fn handle_xml(req: HttpRequest, body: web::Bytes) -> Result {
let user: User = serde_xml_rs::from_reader(body.as_ref())
.map_err(|e| HttpError::BadRequest(e.to_string()))?;
Ok(HttpResponse::Ok().json(user))
}
Content-Type 不匹配导致解析失败的常见表现
如果客户端发的是 text/xml,而你代码里没校验或没适配,serde_xml_rs 本身不会报 MIME 错误,但 Actix-web 可能在中间件或 CORS 预检阶段就拦掉请求。更隐蔽的问题是:某些 HTTP 客户端(如 curl)默认不带 Content-Type,此时 Actix-web 会 fallback 到 application/octet-stream,body 虽能读到,但语义已丢失 —— 你需要显式检查 req.headers().get("content-type")。
- 推荐在 handler 开头加校验:
if req.content_type() != "application/xml" && req.content_type() != "text/xml" { return Err(HttpError::UnsupportedMediaType(...)); } -
req.content_type()返回的是Option,需用.and_then(|v| v.to_str().ok())安全转换 - 测试时用 curl 发送:
curl -X POST -H "Content-Type: application/xml" --data-binary @input.xml http://localhost:8080/xml
为什么不用 quick-xml 替代 serde_xml_rs?
serde_xml_rs 是唯一与 serde 生态无缝对接的 XML 库,支持标准 derive 宏;quick-xml 更快、更底层,但需要手写事件循环或绑定到 serde 的 Deserializer 实现 —— 对多数业务场景属于过度设计。除非你处理 GB 级 XML 流或需要 SAX 式逐节点处理,否则坚持用 serde_xml_rs 更稳。
另外注意:serde_xml_rs 目前不支持 namespace(如 ),若接口协议含 namespace,就得切到 quick-xml + 手动映射,这是最容易被忽略的兼容性断点。










