4

我想在 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`
4

3 回答 3

3

基于 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))
                }),
        )
    }                      
于 2019-10-01T22:12:43.473 回答
3

在 actix-web Gitter 频道的优秀人员的帮助下,我找到了这个解决方案,我也为此做了 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))
                }),
        )
    }
}
于 2019-09-11T13:19:23.780 回答
1

Payloadimplements Stream<Item = Bytes, Error = _>,所以没有理由不能使用与其他框架相同的技巧:

req_
    .take_payload().concat2()
    .and_then(|bytes| {
         info!("request body: {:?}", bytes);
    });

也就是说,如果您有一个正确Payload的 POST/PUT 请求。自从您使用 以来wrap_fn(),您已经有效地设置了一个中间件。这些运行在所有请求中,并且不允许您访问Payload(部分原因是您只能接受一次)。

因此,我认为你不走运。

于 2019-09-10T12:14:02.310 回答