-
Hey folks 👋! The title already tells what I am looking for, basically I have this bit of code, but I would say that only the // ...
forward_ready!(service);
fn call(&self, mut req: ServiceRequest) -> Self::Future {
let service = Rc::clone(&self.service);
Box::pin(async move {
let mut res: ServiceResponse<B> = service.call(req).await?;
res = manipulate_body(&res);
Ok(res)
})
}
// ...
fn manipulate_body<B: MessageBody>(res: &ServiceResponse<B>) -> ServiceResponse<B> {
// Read body bytes
let body_bytes = res.response().body();
let mut new_body: Value = serde_json::from_slice(&body_bytes).unwrap();
new_body["test"] = Value::String("anything".to_owned());
// Destructures ServiceResponse into request and response components
let (req, res) = res.into_parts();
// Create a new response with the modified body
let res: HttpResponse<B> = res.set_body(&new_body.to_string()).map_into_boxed_body();
// Create a new ServiceResponse with the modified response
ServiceResponse::new(req, res).map_into_right_body()
} |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 7 replies
-
There's no way to get a body from a shared So, more or less... fn call(&self, mut req: ServiceRequest) -> Self::Future {
let service = Rc::clone(&self.service);
Box::pin(async move {
let res = service.call(req).await?;
let (req, res) = res.into_parts();
let (res, body) = res.into_parts();
let body = convert_body(body).await;
let res = res.set_body(body);
Ok(ServiceResponse::new(req, res))
})
}
// ...
async fn convert_body<B: MessageBody>(body: B) -> String {
// Read body bytes
let body_bytes = actix_web::body::to_bytes_limited(
body,
2_usize.pow(22), // 4 MB
)
.await
.unwrap()
.map_err(Into::into)
.unwrap();
let mut new_body: Value = serde_json::from_slice(&body_bytes).unwrap();
new_body["test"] = Value::String("anything".to_owned());
new_body.to_string()
} If you are completely sure you'll never return a streaming body, then you could get away with |
Beta Was this translation helpful? Give feedback.
-
I'm not getting a result with that, supposedly as error[E0308]: mismatched types
--> src/middlewares/test_middleware.rs:245:33
|
224 | async fn manipulate_body<B: MessageBody>(res: &ServiceResponse<B>) -> ServiceResponse<B> {
| - expected this type parameter
...
245 | let res = res.set_body::<B>(body.to_string());
| ------------- ^^^^^^^^^^^^^^^^ expected type parameter `B`, found `String`
| |
| arguments to this method are incorrect
|
= note: expected type parameter `B`
found struct `std::string::String` This is what I have as a middleware declaration: impl<S, B> Service<ServiceRequest> for InnerTestMiddleware<S>
where
S: Service<ServiceRequest, Response=ServiceResponse<B>, Error=Error> + 'static,
S::Future: 'static,
B: MessageBody
{
// ... And the current code at the moment with your suggestions. async fn manipulate_body<B: MessageBody>(res: &ServiceResponse<B>) -> ServiceResponse<B> {
let (req, res) = res.into_parts();
let (res, body) = res.into_parts();
// Read body bytes
let body_bytes = actix_web::body::to_bytes_limited(
body,
2_usize.pow(22), // 4 MB
)
.await
.unwrap()
.map_err(Into::into)
.unwrap();
let mut body: Value = serde_json::from_slice(&body_bytes).unwrap();
body["test"] = Value::String("anything".to_owned());
let res = res.set_body::<B>(body.to_string());
ServiceResponse::new(req, res)
} |
Beta Was this translation helpful? Give feedback.
-
Ooohhh yeah, I managed to get everything working with the help of @robjtede, if it wasn't for him it might have taken me longer to get there. Basically, most of the problem I had was that I was restricting the generic type use std::future::{Future, ready, Ready};
use std::pin::Pin;
use std::rc::Rc;
use actix_web::{dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform}, Error};
use actix_web::body::{BoxBody, MessageBody, to_bytes};
use serde_json::Value;
#[derive(Clone, Default)]
pub struct BodyManipulationMiddleware;
impl<S> Transform<S, ServiceRequest> for BodyManipulationMiddleware
where
S: Service<ServiceRequest, Response=ServiceResponse, Error=Error> + 'static,
S::Future: 'static
{
type Response = ServiceResponse;
type Error = Error;
type Transform = InnerBodyManipulationMiddleware<S>;
type InitError = ();
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(InnerBodyManipulationMiddleware {
service: Rc::new(service)
}))
}
}
pub struct InnerBodyManipulationMiddleware<S> {
service: Rc<S>
}
impl<S> Service<ServiceRequest> for InnerBodyManipulationMiddleware<S>
where
S: Service<ServiceRequest, Response=ServiceResponse, Error=Error> + 'static,
S::Future: 'static
{
type Response = ServiceResponse;
type Error = Error;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
forward_ready!(service);
fn call(&self, req: ServiceRequest) -> Self::Future {
let service = Rc::clone(&self.service);
Box::pin(async move {
let res = service.call(req).await?;
Ok(manipulate_body(res).await)
})
}
}
async fn manipulate_body(res: ServiceResponse) -> ServiceResponse {
let (req, res) = res.into_parts();
let (res, body) = res.into_parts();
let body_bytes = to_bytes(body).await.unwrap();
let mut body: Value = serde_json::from_slice(&body_bytes).unwrap();
body["test"] = Value::String("anything".to_owned());
let res = res.set_body(BoxBody::new(body.to_string()));
ServiceResponse::new(req, res)
} |
Beta Was this translation helpful? Give feedback.
Ooohhh yeah, I managed to get everything working with the help of @robjtede, if it wasn't for him it might have taken me longer to get there. Basically, most of the problem I had was that I was restricting the generic type
B
to be aMessageBody
, so if we look at the structServiceResponse
, the generic type is already defined as aBoxBody
and that makes it even better than defining our generic types, making it dynamic and being able to manipulate any types we want. So for those interested who want to know what the solution looks like with the full code, here goes! Happy coding! 💪