基于axum 0.7与reqwest 0.12的HTTP1代理类型不匹配问题求解
解决Axum 0.7.5与Reqwest 0.12的代理类型不匹配问题
问题背景
尝试用Axum 0.7.5和Reqwest 0.12构建HTTP代理时,出现类型不匹配错误,核心代码如下:
#[tokio::main] async fn main() { let app = Router::new() .route("/", get(handler)) .route("/custom/*key", get(proxy).post(proxy)); // ...(省略服务器启动代码) } pub async fn proxy(mut req: Request<Body>) -> Result<Response<Body>, StatusCode> { let path_query = req .uri() .path_and_query() .map_or_else(|| req.uri().path(), |v| v.as_str()); let uri = format!("http://localhost:9000/{}", path_query); *req.uri_mut() = Uri::try_from(uri).unwrap(); let http_client = reqwest::Client::new(); let res = http_client.execute(req).await.unwrap(); if res.status().is_server_error() { return Err(StatusCode::INTERNAL_SERVER_ERROR); } Ok(res) }
错误信息
编译时出现两个类型不匹配错误:
第一个错误:
error[E0308]: mismatched types | 35 | let res = http_client.execute(req).await.unwrap(); | ------- ^^^ expected `Request`, found `Request<Incoming>` | | | arguments to this method are incorrect | = note: `Request<Incoming>` and `reqwest::Request` have similar names, but are actually distinct types note: `Request<Incoming>` is defined in crate `http` --> C:\Users\Fred\.cargo\registry\src\index.crates.io-6f17d22bba15001f\http-1.1.0\src\request.rs:158:1 | 158 | pub struct Request<T> { | ^^^^^^^^^^^^^^^^^^^^^ note: `reqwest::Request` is defined in crate `reqwest` --> C:\Users\Fred\.cargo\registry\src\index.crates.io-6f17d22bba15001f\reqwest-0.12.4\src\async_impl\request.rs:22:1 | 22 | pub struct Request { | ^^^^^^^^^^^^^^^^^^
第二个错误:
error[E0308]: mismatched types | 43 | Ok(res) | -- ^^^ expected `Response<Incoming>`, found `Response` | | | arguments to this enum variant are incorrect | = note: `reqwest::Response` and `Response<Incoming>` have similar names, but are actually distinct types note: `reqwest::Response` is defined in crate `reqwest` --> C:\Users\Fred\.cargo\registry\src\index.crates.io-6f17d22bba15001f\reqwest-0.12.4\src\async_impl\response.rs:29:1
问题原因
Axum使用的是http::Request<axum::body::Body>(即Request<Incoming>),而Reqwest的execute方法接收的是reqwest::Request;同理,Reqwest返回的reqwest::Response也无法直接转换成Axum需要的http::Response<axum::body::Body>。两者虽基于同一http crate,但Reqwest对请求/响应结构体做了封装,不能直接互换。
解决方案
手动完成两种请求/响应类型的转换,修改后的proxy函数如下:
use axum::{ body::{Body, Bytes}, http::{Request, Response, StatusCode, Uri}, routing::{get, post}, Router, }; use reqwest::{Client, IntoUrl}; use std::convert::TryInto; #[tokio::main] async fn main() { let app = Router::new() .route("/", get(handler)) .route("/custom/*key", get(proxy).post(proxy)); axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) .serve(app.into_make_service()) .await .unwrap(); } async fn handler() -> &'static str { "Hello from root!" } pub async fn proxy(req: Request<Body>) -> Result<Response<Body>, StatusCode> { // 提取路径和查询参数 let path_query = req .uri() .path_and_query() .map_or_else(|| req.uri().path(), |v| v.as_str()); // 构造目标URI let target_uri = format!("http://localhost:9000/{}", path_query) .try_into() .map_err(|_| StatusCode::BAD_REQUEST)?; // 全局初始化客户端更优,这里为示例简洁性直接创建 let client = Client::new(); // 转换Axum请求为Reqwest请求构建器 let mut req_builder = client.request(req.method().clone(), target_uri); for (name, value) in req.headers() { req_builder = req_builder.header(name, value); } // 读取并转换请求体 let body_bytes = match req.into_body().collect().await { Ok(collected) => collected.to_bytes(), Err(_) => return Err(StatusCode::BAD_REQUEST), }; // 发送请求 let res = req_builder.body(body_bytes) .send() .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; if res.status().is_server_error() { return Err(StatusCode::INTERNAL_SERVER_ERROR); } // 转换Reqwest响应为Axum响应 let mut axum_res = Response::builder().status(res.status()); for (name, value) in res.headers() { axum_res = axum_res.header(name, value); } let res_body = res.bytes().await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; axum_res.body(Body::from(res_body)) .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) }
关键修改点说明
- 请求转换:拆解Axum请求的方法、URI、头部和体,用Reqwest的
RequestBuilder重新构建请求。 - 响应转换:提取Reqwest响应的状态码、头部和体,构建Axum兼容的响应结构。
- 性能优化:建议将
reqwest::Client全局初始化(比如在main中创建后传入代理函数),避免每次请求重复创建客户端。
内容的提问来源于stack exchange,提问作者Fred Hors
相关产品推荐
相关产品推荐

