use std::{ future::{ready, Ready}, rc::Rc, }; use actix_http::h1; use actix_web::{ dev::{self, Service, ServiceRequest, ServiceResponse, Transform}, web, Error, }; use futures_util::future::LocalBoxFuture; pub struct Logging; impl Transform for Logging where S: Service, Error = Error>, S::Future: 'static, B: 'static, { type Response = ServiceResponse; type Error = Error; type InitError = (); type Transform = LoggingMiddleware; type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { ready(Ok(LoggingMiddleware { service: Rc::new(service), })) } } pub struct LoggingMiddleware { // This is special: We need this to avoid lifetime issues. service: Rc, } impl Service for LoggingMiddleware where S: Service, Error = Error> + 'static, S::Future: 'static, B: 'static, { type Response = ServiceResponse; type Error = Error; type Future = LocalBoxFuture<'static, Result>; dev::forward_ready!(service); fn call(&self, mut req: ServiceRequest) -> Self::Future { let svc = self.service.clone(); Box::pin(async move { // extract bytes from request body let body = req.extract::().await.unwrap(); println!("request body (middleware): {body:?}"); // re-insert body back into request to be used by handlers req.set_payload(bytes_to_payload(body)); let res = svc.call(req).await?; println!("response: {:?}", res.headers()); Ok(res) }) } } fn bytes_to_payload(buf: web::Bytes) -> dev::Payload { let (_, mut pl) = h1::Payload::create(true); pl.unread_data(buf); dev::Payload::from(pl) }