//! Run with //! //! ```not_rust //! cargo run -p example-print-request-response //! ``` use axum::{ body::{Body, Bytes}, extract::Request, http::StatusCode, middleware::{self, Next}, response::{IntoResponse, Response}, routing::post, Router, }; use http_body_util::BodyExt; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] async fn main() { tracing_subscriber::registry() .with( tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| { format!("{}=debug,tower_http=debug", env!("CARGO_CRATE_NAME")).into() }), ) .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 listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await .unwrap(); tracing::debug!("listening on {}", listener.local_addr().unwrap()); axum::serve(listener, app).await.unwrap(); } async fn print_request_response( req: Request, next: Next, ) -> Result { 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(direction: &str, body: B) -> Result where B: axum::body::HttpBody, B::Error: std::fmt::Display, { let bytes = match body.collect().await { Ok(collected) => collected.to_bytes(), Err(err) => { return Err(( StatusCode::BAD_REQUEST, format!("failed to read {direction} body: {err}"), )); } }; if let Ok(body) = std::str::from_utf8(&bytes) { tracing::debug!("{direction} body = {body:?}"); } Ok(bytes) }