I want to read out the body in a middleware in actix-web 1.0. I'm using the closure-style middleware using wrap_fn
.
My basic setup is this:
let mut server = HttpServer::new(move || {
ActixApp::new()
.wrap_fn(|req, srv| {
srv.call(req).map(|res| {
let req_ = res.request();
let body = req_.magical_body_read_function();
dbg!(body);
res
})
})
});
I need that magical_body_read_function()
which sadly doesn't exist.
I hacked together something that looks like it could work by reading the examples and using take_payload()
but it didn't work, sadly:
let mut server = HttpServer::new(move || {
ActixApp::new()
.wrap_fn(|req, srv| {
srv.call(req).map(|res| {
let req_ = res.request();
req_.take_payload()
.fold(BytesMut::new(), move |mut body, chunk| {
body.extend_from_slice(&chunk);
Ok::<_, PayloadError>(body)
})
.and_then(|bytes| {
info!("request body: {:?}", bytes);
});
res
})
})
});
Gives me
error[E0599]: no method named `fold` found for type `actix_http::payload::Payload<()>` in the current scope --> src/main.rs:209:26
| 209 | .fold(BytesMut::new(), move |mut body, chunk| {
| ^^^^
|
= note: the method `fold` exists but the following trait bounds were not satisfied:
`&mut actix_http::payload::Payload<()> : std::iter::Iterator`
I then tried an approach using the full middleware:
pub struct Logging;
impl<S, B> Transform<S> for Logging
where
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Request = ServiceRequest;
type Response = ServiceResponse<B>;
type Error = Error;
type InitError = ();
type Transform = LoggingMiddleware<S>;
type Future = FutureResult<Self::Transform, Self::InitError>;
fn new_transform(&self, service: S) -> Self::Future {
ok(LoggingMiddleware { service })
}
}
pub struct LoggingMiddleware<S> {
service: S,
}
impl<S, B> Service for LoggingMiddleware<S>
where
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Request = ServiceRequest;
type Response = ServiceResponse<B>;
type Error = Error;
type Future = Box<dyn Future<Item = Self::Response, Error = Self::Error>>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
self.service.poll_ready()
}
fn call(&mut self, req: ServiceRequest) -> Self::Future {
Box::new(self.service.call(req).and_then(|res| {
let req_ = res.request();
req_.take_payload()
.fold(BytesMut::new(), move |mut body, chunk| {
body.extend_from_slice(&chunk);
Ok::<_, PayloadError>(body)
})
.and_then(|bytes| {
info!("request body: {:?}", bytes);
});
Ok(res)
}))
}
}
which sadly also resulted in the very similar looking error:
error[E0599]: no method named `fold` found for type `actix_http::payload::Payload<()>` in the current scope
--> src/main.rs:204:18
|
204 | .fold(BytesMut::new(), move |mut body, chunk| {
| ^^^^
|
= note: the method `fold` exists but the following trait bounds were not satisfied:
`&mut actix_http::payload::Payload<()> : futures::stream::Stream`
`&mut actix_http::payload::Payload<()> : std::iter::Iterator`
`actix_http::payload::Payload<()> : futures::stream::Stream`
With the help of the fine people in the actix-web Gitter channel, I came to this solution which I also made a PR for.
Full solution is:
pub struct Logging;
impl<S: 'static, B> Transform<S> for Logging
where
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Request = ServiceRequest;
type Response = ServiceResponse<B>;
type Error = Error;
type InitError = ();
type Transform = LoggingMiddleware<S>;
type Future = FutureResult<Self::Transform, Self::InitError>;
fn new_transform(&self, service: S) -> Self::Future {
ok(LoggingMiddleware {
service: Rc::new(RefCell::new(service)),
})
}
}
pub struct LoggingMiddleware<S> {
// This is special: We need this to avoid lifetime issues.
service: Rc<RefCell<S>>,
}
impl<S, B> Service for LoggingMiddleware<S>
where
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>
+ 'static,
S::Future: 'static,
B: 'static,
{
type Request = ServiceRequest;
type Response = ServiceResponse<B>;
type Error = Error;
type Future = Box<dyn Future<Item = Self::Response, Error = Self::Error>>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
self.service.poll_ready()
}
fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
let mut svc = self.service.clone();
Box::new(
req.take_payload()
.fold(BytesMut::new(), move |mut body, chunk| {
body.extend_from_slice(&chunk);
Ok::<_, PayloadError>(body)
})
.map_err(|e| e.into())
.and_then(move |bytes| {
println!("request body: {:?}", bytes);
svc.call(req).and_then(|res| Ok(res))
}),
)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With