如何在 actix-web 1.0 中间件中读取请求的主体?
How to read a request's body in an actix-web 1.0 middleware?
我想在actix-web 1.0 的中间件中读出正文。我正在使用 wrap_fn
.
的闭包式中间件
我的基本设置是这样的:
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
})
})
});
我需要那个 magical_body_read_function()
,遗憾的是它不存在。
我通过阅读示例和使用 take_payload()
拼凑了一些看起来可行的东西,但不幸的是它没有用:
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
})
})
});
给我
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`
然后我尝试了一种使用完整中间件的方法:
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)
}))
}
}
遗憾的是,这也导致了看起来非常相似的错误:
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`
Payload
实现了 Stream<Item = Bytes, Error = _>
,因此您没有理由不能使用与其他框架相同的技巧:
req_
.take_payload().concat2()
.and_then(|bytes| {
info!("request body: {:?}", bytes);
});
也就是说,如果您从 POST/PUT 请求中获得了正确的 Payload
。由于您使用了 wrap_fn()
,您已经有效地设置了一个中间件。那些 运行 跨所有请求,并且不允许您访问 Payload
(部分原因是您只能接受一次)。
因此,我认为你运气不好。
在 actix-web Gitter 频道中的好人的帮助下,我找到了这个解决方案,我也为此做了 a PR。
完整的解决方案是:
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))
}),
)
}
}
在 svenstaro 的解决方案的基础上,您可以执行类似以下操作以在克隆剥离的字节后重建请求。
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);
let mut payload = actix_http::h1::Payload::empty();
payload.unread_data(bytes.into());
req.set_payload(payload.into());
svc.call(req).and_then(|res| Ok(res))
}),
)
}
我想在actix-web 1.0 的中间件中读出正文。我正在使用 wrap_fn
.
我的基本设置是这样的:
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
})
})
});
我需要那个 magical_body_read_function()
,遗憾的是它不存在。
我通过阅读示例和使用 take_payload()
拼凑了一些看起来可行的东西,但不幸的是它没有用:
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
})
})
});
给我
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`
然后我尝试了一种使用完整中间件的方法:
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)
}))
}
}
遗憾的是,这也导致了看起来非常相似的错误:
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`
Payload
实现了 Stream<Item = Bytes, Error = _>
,因此您没有理由不能使用与其他框架相同的技巧:
req_
.take_payload().concat2()
.and_then(|bytes| {
info!("request body: {:?}", bytes);
});
也就是说,如果您从 POST/PUT 请求中获得了正确的 Payload
。由于您使用了 wrap_fn()
,您已经有效地设置了一个中间件。那些 运行 跨所有请求,并且不允许您访问 Payload
(部分原因是您只能接受一次)。
因此,我认为你运气不好。
在 actix-web Gitter 频道中的好人的帮助下,我找到了这个解决方案,我也为此做了 a PR。
完整的解决方案是:
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))
}),
)
}
}
在 svenstaro 的解决方案的基础上,您可以执行类似以下操作以在克隆剥离的字节后重建请求。
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);
let mut payload = actix_http::h1::Payload::empty();
payload.unread_data(bytes.into());
req.set_payload(payload.into());
svc.call(req).and_then(|res| Ok(res))
}),
)
}