Rust Actix Web同时提取JSON时Bytes提取器返回空字节的问题
Actix Web同时提取Json和Bytes时Bytes为空的解决办法
问题原因
Actix Web的请求体是一次性可读流:当你通过web::Json提取器完成反序列化时,整个请求体的字节已经被读取并消耗完毕,后续再尝试提取web::Bytes自然只能得到空数据。
解决方案
方法一:先提取Bytes,手动反序列化JSON
直接先获取完整的请求体字节,再自行用serde_json完成反序列化,同时保留原始字节数据:
use actix_web::*; use serde_json::Value; #[tokio::main] async fn main() -> std::io::Result<()> { use std::net::{IpAddr, Ipv4Addr, SocketAddr}; let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 3000); HttpServer::new(move || App::new().service(example)) .bind(&addr)? .run() .await } #[post("/")] pub async fn example( payload_bytes: web::Bytes, ) -> Result<HttpResponse, Error> { // 手动反序列化JSON,失败则返回BadRequest错误 let json_payload: Value = serde_json::from_slice(&payload_bytes) .map_err(|e| error::ErrorBadRequest(format!("无效JSON格式: {}", e)))?; println!("请求体字节长度: {}", payload_bytes.len()); println!("解析后的JSON: {}", json_payload); Ok(HttpResponse::Ok().into()) }
方法二:自定义提取器(复用场景)
如果需要在多个路由中复用“同时获取原始字节和结构化JSON”的逻辑,可以自定义提取器封装这一流程:
use actix_web::{dev::Payload, error, web, Error, FromRequest, HttpResponse}; use serde::de::DeserializeOwned; use serde_json::Value; use futures_util::StreamExt; // 自定义提取器,同时保存原始字节和反序列化后的JSON #[derive(Debug)] struct JsonWithBytes<T> { pub raw_bytes: web::Bytes, pub parsed_json: T, } impl<T: DeserializeOwned> FromRequest for JsonWithBytes<T> { type Error = Error; type Future = std::pin::Pin<Box<dyn std::future::Future<Output = Result<Self, Self::Error>>>>; fn from_request(req: &web::HttpRequest, payload: &mut Payload) -> Self::Future { let mut payload = payload.take(); Box::pin(async move { // 读取全部请求体字节 let mut bytes_buf = web::BytesMut::new(); while let Some(chunk) = payload.next().await { bytes_buf.extend_from_slice(&chunk?); } let raw_bytes = bytes_buf.freeze(); // 反序列化JSON let parsed_json = serde_json::from_slice(&raw_bytes) .map_err(|e| error::ErrorBadRequest(format!("无效JSON格式: {}", e)))?; Ok(JsonWithBytes { raw_bytes, parsed_json }) }) } } // 使用自定义提取器的路由 #[post("/")] pub async fn example( data: JsonWithBytes<Value>, ) -> Result<HttpResponse, Error> { println!("请求体字节长度: {}", data.raw_bytes.len()); println!("解析后的JSON: {}", data.parsed_json); Ok(HttpResponse::Ok().into()) } #[tokio::main] async fn main() -> std::io::Result<()> { use std::net::{IpAddr, Ipv4Addr, SocketAddr}; let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 3000); HttpServer::new(move || App::new().service(example)) .bind(&addr)? .run() .await }
测试命令
执行以下命令验证效果:
curl -X POST -H "Content-Type: application/json" -d '{"message":"hello world"}' http://localhost:3000
内容的提问来源于stack exchange,提问作者pat.io
相关产品推荐
相关产品推荐

