如何在Hyper中接收POST请求的表单及文件数据?
在Hyper中接收POST请求的表单数据(含文件上传)
场景说明
需要处理的POST请求(通过curl发送):
curl -X POST -F "field=@/path/to/file.txt" -F "tool=curl" -F "other-file=@/path/to/other.jpg" http://localhost:8000
现有Hyper基础代码:
use std::{convert::Infallible, net::SocketAddr}; use hyper::{Body, Request, Response, Server}; use hyper::service::{make_service_fn, service_fn}; async fn handle(_: Request<Body>) -> Result<Response<Body>, Infallible> { Ok(Response::new("Hello, World!".into())) } #[tokio::main] async fn main() { let addr = SocketAddr::from(([127, 0, 0, 1], 8000)); let make_svc = make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(handle)) }); let server = Server::bind(&addr).serve(make_svc); if let Err(e) = server.await { eprintln!("server error: {}", e); } }
遇到的问题
尝试用multipart crate处理请求时,编写了如下代码:
async fn handle(req: Request<Body>) -> Result<Response<Body>, Infallible> { let mut files = multipart::server::Multipart::from(req); ..... }
出现错误:
expected struct
multipart::server::Multipart, found structhyper::Request
解决方案
1. 添加正确的依赖
原multipart crate不直接兼容Hyper的Request类型,需要使用multipart-rfc7578(专门适配Hyper等异步HTTP库的multipart处理库),同时保留hyper和tokio依赖。在Cargo.toml中添加:
[dependencies] hyper = { version = "1.1", features = ["full"] } tokio = { version = "1.0", features = ["full"] } multipart-rfc7578 = "0.6" bytes = "1.0"
2. 修改请求处理逻辑
核心步骤:
- 判断请求方法是否为POST
- 检查
Content-Type是否为multipart/form-data - 从请求中提取multipart流,逐个解析字段和文件
修改后的完整代码:
use std::{convert::Infallible, net::SocketAddr}; use hyper::{Body, Request, Response, Server, StatusCode}; use hyper::service::{make_service_fn, service_fn}; use multipart_rfc7578::server::{Multipart, Field}; use bytes::Bytes; async fn handle(req: Request<Body>) -> Result<Response<Body>, Infallible> { // 仅处理POST请求 if req.method() != hyper::Method::POST { return Ok(Response::builder() .status(StatusCode::METHOD_NOT_ALLOWED) .body("Only POST is allowed".into()) .unwrap()); } // 解析multipart请求 match Multipart::try_from(req).await { Ok(mut multipart) => { let mut result = String::new(); // 遍历所有字段 while let Some(field) = multipart.next_field().await.unwrap() { match field { Field::Text(name, value) => { // 处理文本字段 let text = String::from_utf8(value.into_bytes().to_vec()).unwrap(); result.push_str(&format!("Text field '{}': {}\n", name, text)); } Field::File(name, file) => { // 处理文件字段 let filename = file.file_name().unwrap_or("unknown").to_string(); // 读取文件内容(示例转为Bytes,可按需写入磁盘) let content = file.into_bytes().await.unwrap(); result.push_str(&format!("File field '{}' ({}): {} bytes\n", name, filename, content.len())); } } } Ok(Response::new(result.into())) } Err(e) => { // 处理解析错误 Ok(Response::builder() .status(StatusCode::BAD_REQUEST) .body(format!("Multipart parse error: {}", e).into()) .unwrap()) } } } #[tokio::main] async fn main() { let addr = SocketAddr::from(([127, 0, 0, 1], 8000)); let make_svc = make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(handle)) }); let server = Server::bind(&addr).serve(make_svc); if let Err(e) = server.await { eprintln!("server error: {}", e); } }
3. 错误原因解释
你之前的错误是因为multipart::server::Multipart仅支持该 crate 自身定义的请求类型,无法直接接收Hyper的Request<Body>。multipart-rfc7578库做了专门适配,提供Multipart::try_from(req)方法,可直接从Hyper请求对象创建解析器,解决了类型不兼容的问题。
内容的提问来源于stack exchange,提问作者DFG
相关产品推荐
相关产品推荐

