Axum中间件记录响应体时遭遇类型不匹配问题求助
问题:Axum中间件记录请求/响应体时的类型不匹配错误
我想实现一个记录HTTP请求和响应体的Axum中间件,参考官方示例编写后遇到编译错误,提示类型不匹配:
error[E0308]: mismatched types --> src\core.rs:302:24 | 294 | async fn print_request_response<B>( | - this type parameter ... 302 | let res = next.run(req).await; | --- ^^^ expected type parameter `B`, found struct `Body` | | | arguments to this function are incorrect | = note: expected struct `hyper::Request<B>` found struct `hyper::Request<Body>`
尝试调整类型参数和请求体构造方式都没解决,只要能实现日志功能即可,以下是最小复现代码:
Cargo.toml
[package] name = "test" version = "0.1.0" edition = "2021" [dependencies] axum = { version = "0.6.18", features = ["http2"] } hyper = { version = "0.14", features = ["full"] } tokio = { version = "1.0", features = ["full"] } tower = { version = "0.4", features = ["util", "filter"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] }
main.rs
use std::net::SocketAddr; use axum::{ body::{Body, Bytes}, http::StatusCode, middleware::{self, Next}, response::{IntoResponse, Response}, routing::post, Router, }; use hyper::Request; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] async fn main() { let app = Router::new() .route("/", post(|| async move { "Hello from `POST /`" })) .layer(middleware::from_fn(print_request_response)); let addr = SocketAddr::from(([0, 0, 0, 0], 8080)); axum::Server::bind(&addr) .serve(app.into_make_service()) .await .unwrap(); } async fn print_request_response<B>( req: Request<B>, next: Next<B>, ) -> Result<impl IntoResponse, (StatusCode, String)> { let (parts, body) = req.into_parts(); let bytes = buffer_and_print("request", body).await?; let req = Request::from_parts(parts, Body::from(bytes)); let res = next.run(req).await; let (parts, body) = res.into_parts(); let bytes = buffer_and_print("response", body).await?; let res = Response::from_parts(parts, Body::from(bytes)); Ok(res) } async fn buffer_and_print<B>(direction: &str, body: B) -> Result<Bytes, (StatusCode, String)> { let bytes = match hyper::body::to_bytes(body).await { Ok(bytes) => bytes, Err(err) => { return Err(( StatusCode::BAD_REQUEST, format!("failed to read {} body: {}", direction, err), )); } }; if let Ok(body) = std::str::from_utf8(&bytes) { tracing::debug!("{} body = {:?}", direction, body); } Ok(bytes) }
解决方案
问题根源
中间件定义了泛型参数B,要求传入的请求体类型和Next<B>期望的类型完全一致,但读取原请求体后重新构造了Request<Body>,和原类型B不匹配,导致编译错误。
修复方案
Axum默认使用axum::body::Body作为请求体类型,直接指定中间件使用Body类型即可解决类型不匹配问题,同时给buffer_and_print添加正确的HttpBody约束,确保能处理合法的请求/响应体。
修改后的完整代码
use std::net::SocketAddr; use axum::{ body::{Body, Bytes}, http::StatusCode, middleware::{self, Next}, response::{IntoResponse, Response}, routing::post, Router, }; use hyper::{Request, body::HttpBody}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] async fn main() { // 初始化tracing日志,确保能输出debug级别的日志 tracing_subscriber::registry() .with(tracing_subscriber::EnvFilter::new("debug")) .with(tracing_subscriber::fmt::layer()) .init(); let app = Router::new() .route("/", post(|| async move { "Hello from `POST /`" })) .layer(middleware::from_fn(print_request_response)); let addr = SocketAddr::from(([0, 0, 0, 0], 8080)); axum::Server::bind(&addr) .serve(app.into_make_service()) .await .unwrap(); } async fn print_request_response( req: Request<Body>, next: Next<Body>, ) -> Result<impl IntoResponse, (StatusCode, String)> { let (parts, body) = req.into_parts(); let bytes = buffer_and_print("request", body).await?; // 构造和Next<Body>匹配的Request<Body> let req = Request::from_parts(parts, Body::from(bytes)); let res = next.run(req).await; let (parts, body) = res.into_parts(); let bytes = buffer_and_print("response", body).await?; let res = Response::from_parts(parts, Body::from(bytes)); Ok(res) } async fn buffer_and_print<B>(direction: &str, body: B) -> Result<Bytes, (StatusCode, String)> where B: HttpBody + Send + 'static, B::Error: std::fmt::Display, { let bytes = match hyper::body::to_bytes(body).await { Ok(bytes) => bytes, Err(err) => { return Err(( StatusCode::BAD_REQUEST, format!("failed to read {} body: {}", direction, err), )); } }; if let Ok(body) = std::str::from_utf8(&bytes) { tracing::debug!("{} body = {:?}", direction, body); } Ok(bytes) }
关键修改点
- 移除中间件的泛型参数
B,直接使用Request<Body>和Next<Body>,确保类型完全匹配。 - 给
buffer_and_print添加HttpBody相关约束,保证函数能处理任何符合Hyper规范的请求/响应体。 - 初始化tracing日志系统,确保能看到debug级别的请求/响应体日志。
内容的提问来源于stack exchange,提问作者Tennie
相关产品推荐
相关产品推荐

