I want to create a middleware of actix_web. I followed sample and come up below code that works fine.
fn call(&mut self, req: ServiceRequest) -> Self::Future {
println!("AuthZ middleware started for request: {}.", req.path());
let fut = self.service.call(req);
Box::pin(async move {
let res = fut.await?;
println!("AuthZ middleware ended.");
Ok(res)
})
}
When I want to change the second println! to output the request path as well. The code becomes as below
fn call(&mut self, req: ServiceRequest) -> Self::Future {
println!("AuthZ middleware started for request: {}.", req.path());
let fut = self.service.call(req);
Box::pin(async move {
let res = fut.await?;
println!("AuthZ middleware ended: {}.", req.path());
Ok(res)
})
}
It started to report an error when I used the req after it was moved into self.service.call. I tried to follow some other samples that shows I can pass a value in as reference to make sure the value is borrowed and the ownership will be returned after the call. So, I tried to change it to be self.service.call(&req). This change gave me another error that says somethig like "expected a structure but found a reference, consider removing the borrow".
Based on above experience, I have several questions.
In actix_web, how do I solve this issue if I want to access request info after previous middleware is done? I am guessing maybe something in the return value of self.service.call() may helpful, but I am not sure.
In general, if I want to use a 3rd party function that only accepts a parameter and must take ownership of the value. I also want to access the value after the call to 3rd party function. What should I do?
What is the type of the service, and where does it come from? The Service trait itself is very generic, it looks like different implementations could use references rather than consuming the entire request, but it would depend on the middleware implementation. Whether the request is available on the response will also depend on the which type that the Service is.
In your specific example, you could use the format! macro to format the logging message so that you don’t need the request after the service call, though that’s not terribly ideal.
After something is moved to a function, it's gone for good. That function got full exclusive ownership of the object. That's not even a theoretical limitation: that function could have destroyed the object right away, so it may not even exist after the function call. There's no garbage collector in Rust, so Rust can't make objects live longer for you, you need to change what the code does.
Typical workarounds:
Get the data you need before you give the object away. In your case let path = req.path().to_owned(); to keep your own copy of the path, independent of req.
Some functions allow arguments to be passed either as owned or borrowed. e.g. std::fs::write(path, data) and std::fs::write(&path, &data) are both valid (they have AsRef<Path> as argument type). If you can temporarily lend an object to a function instead of passing ownership, you will be able to use it later.
If these are your functions, and you don't want either of previous solutions, you can wrap object in Arc<O> (read-only) or Arc<Mutex<O>> (read-write) to be able to clone references to it and have it "owned" in multiple places at once.