Modify response inside actix web got error

I had created a middleware for modify response body into struct.
It compiles normally in the current file

use actix_http::body::{EitherBody, MessageBody};
use actix_web::{
    dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
    Error,
};
use futures_util::future::LocalBoxFuture;
use serde::{Deserialize, Serialize};
use std::{
    future::{ready, Ready},
    rc::Rc,
};

#[derive(Debug, Serialize, Deserialize)]
pub struct MyRes<B: 'static> {
    data: B,
}

pub struct MyResponse;

impl<S, B> Transform<S, ServiceRequest> for MyResponse
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
    S::Future: 'static,
    B: 'static + MessageBody,
{
    type Response = ServiceResponse<EitherBody<MyRes<B>>>;
    type Error = Error;
    type InitError = ();
    type Transform = MyResponseMiddleware<S>;
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ready(Ok(MyResponseMiddleware {
            service: Rc::new(service),
        }))
    }
}

pub struct MyResponseMiddleware<S> {
    service: Rc<S>,
}

impl<S, B> Service<ServiceRequest> for MyResponseMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
    S::Future: 'static,
    B: 'static + MessageBody,
{
    type Response = ServiceResponse<EitherBody<MyRes<B>>>;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    forward_ready!(service);

    fn call(&self, mut req: ServiceRequest) -> Self::Future {
        let svc = self.service.clone();

        Box::pin(async move {
            let res = svc.call(req).await?;

            let (cur_req, mut cur_res) = res.into_parts();

            let cur_body = cur_res.into_body();

            let cur_res = cur_res.set_body(MyRes { data: cur_body });
            let new_res = ServiceResponse::new(cur_req, cur_res);

            Ok(new_res.map_into_left_body())
        })
    }
}

but got error in App:new

 App::new()
            .wrap(MyResponse)
            .service(index)
            .configure(admin_config)


error[E0277]: the trait bound `App<impl ServiceFactory<ServiceRequest, Config = (), Response = ServiceResponse<actix_web::middleware::logger::StreamLog<actix_web::middleware::logger::StreamLog<EitherBody<EitherBody<MyRes<BoxBody>>>>>>, Error = actix_web::Error, InitError = ()>>: actix_service::IntoServiceFactory<_, Request>` is not satisfied

I wonder if it's because my MyRef struct doesn't impl MessageBody.

Sounds like a reasonable assumption. Your middleware's Response associated type must be ServiceResponse<X> where X: MessageBody. EitherBody only implements MessageBody, if both the left and right types also implement it. If MyRef does not implement MessageBody, neither will EitherBody, causing the trait bound introduced by App::wrap to be violated.

Also, you don't need EitherBody here, as you only ever return ServiceResponse<MyRes<B>>.

Thank you for reply,
I tried to create a new json HttpResponse,
the struct MyRes has a "data" whitch is used for receive original response body

    #[derive(Debug, Serialize, Deserialize)]
    pub struct MyRes<B: 'static> {
        data: B,
    }

    fn call(&self, req: ServiceRequest) -> Self::Future {
        let svc = self.service.clone();

        Box::pin(async move {
            let res = svc.call(req).await?;

            let (cur_req, mut cur_res) = res.into_parts();

            let http_res = HttpResponse::Created()
                .status(cur_res.status())
                .json(MyRes {
                    data: cur_res.into_body(),
                });
            // .finish();

            let new_res = ServiceResponse::new(cur_req, http_res );

            Ok(new_res)
        })
    }

but the compiler warned me that Serialize is not implemented for type B (original response body) ,
are there any way to deal with it ?

The warning happened here

                .json(MyRes {
                    data: cur_res.into_body(), <----
                });

Try adding the Serialize trait to the bounds of B:

-    B: 'static + MessageBody,
+    B: 'static + MessageBody + Serialize

I had tried this ,
but it seems the wrap function doesn`t provide a boxbody which satisfied Serialize

Hm, maybe you need type annotations:

- let res = svc.call(req).await?;
+ let res: ServiceResponse<B> = svc.call(req).await?;

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.