My issue is that I cannot find a good example of an actix-web client that shows how to retrieve the body of the response.
The following doesn't display the body.
pub fn start(request: &str) {
actix::run(
|| actix_web::client::get(&request) // <- Create request builder
.header("User-Agent", "Actix-web")
.finish().unwrap()
.send()
.conn_timeout(Duration::from_secs(10))// <- Send http request
.map_err(|Error::from| ())
.and_then(handle_response )
);
}
fn handle_response(response: ClientResponse) -> Result<(), ()> {
let fn_name = "handle_response";
println!("{}: Response: {:?}", fn_name, &response);
let body = response.body();
match body
.limit(8048)
.and_then(|bytes: Bytes| { // <- complete body
println!("==== BODY ==== {:?}", bytes);
Ok(())
}).poll() {
Ok(_value) => {
println!("value {:?}", _value);
},
Err(_err) => {
println!("error {:?}", _err);
}
}
Ok(())
}
What I get is the following:
handle_response: Response:
ClientResponse HTTP/1.1 200 OK
headers:
"content-type": "application/json; charset=\"UTF-8\""
"content-length": "3658"
value NotReady
That makes perfect sense, because when dealing with a socket, one would typically need to keep reading to get the next chunk of data. However, I thought that the .poll() would probably handle that.
However, I cannot find any examples on how to setup a future to be able to gather all of the chunks into the final response. Note that I posted on reddit 18 days ago and never received a response.
I suspect that the Response needs to be defined as a future, but I cannot seem to find a syntax that will work with the APIs that I am using.
I think I need to call something like the following, but how do I feed this to actix::run()? Do I have to convert it into an Actor message?
pub fn _query(req: &str) -> Box<Future<Item=String, Error=Error>> {
Box::new(
client::get(&req)
.finish().unwrap()
.send()
.map_err(Error::from)
.and_then(
|resp|resp.body()
.from_err()
.and_then(|body| {
let resp_data = str::from_utf8(&body).unwrap();
println!("{}", resp_data);
future::ok(resp_data.to_string())
})
)
)
}
Any help would be appreciated. A pointer to a comprehensive example would be useful.
Thanks,
-Dave
Sorry, I guess I should have opened this here:
https://github.com/actix/examples/issues
Can it be moved, or do I need to close this and create a new one there?
@crusty-dave
I found some code on the internet. It works like this:
...
client.request(request).and_then(|response| {
match response.status().as_u16() {
200 => match response.into_body().concat2().wait() {
Ok(data) => ...
I forgot the type of data :-D You might find it in futures crate's docs.
But I think wait() is not good in asynchronous context. Please do your research about that call. For me, I was too lazy, and is still using this technique in production code. :-(
@hkcorac
The syntax that you provided didn't work, the best I could come up with was the following, but I still don't get the body of the response:
fn _handle_response2(response: client::ClientResponse) -> Result<(), ()> {
let fn_name = "handle_response";
println!("{}: Response: {:?}", fn_name, &response);
match response.status() {
StatusCode::OK => {
match response.body().wait() {
Ok(body) => {
let _foo = body.to_vec();
println!("==== BODY ==== {:?}", _foo);
Ok(())
},
Err(_err) => {
println!("error {:?}", _err);
Err(())
}
}
},
_ => {
println!("error {:?}", response.status());
Err(())
}
}
}
It seems like the wait would need to be outside of the handler, but I couldn't get that to compile:
pub fn start(request: &str) {
actix::run(
|| client::get(&jims_request) // <- Create request builder
.header("User-Agent", "Actix-web")
.finish().unwrap()
.send()
//.conn_timeout(Duration::from_secs(30))// <- Send http request
.map_err(|_| () )
.and_then(handle_response ).wait()
);
}
error[E0277]: the trait bound
std::result::Result<(), ()>: futures::Futureis not satisfied
--> src\srx_sim.rs:78:9
|
78 | actix::run(
| ^^^^^^^^^^ the traitfutures::Futureis not implemented forstd::result::Result<(), ()>
|
= note: required byactix::run
The following is the version from Cargo.toml:
actix = "0.7.4"
actix-web = { version="0.7.7", features=["flate2-rust"], default-features = false }
Thanks for trying to help,
-Dave
Please learn a bit more about how to handle futures
response.body() returns future as you can see, but you cannot just wait on it.
Instead I'd suggest to do following:
and_then(|rsp| rsp.body()).and_then|body| {
println!("Handle body");
Ok(())
}
The best way to work with futures is to change them
For further questions feel free to visit our gitter
@DoumanAsh I've tried to help because I faced the same problem like his before. Personally I think Issues section can be used for question like this. Again, it's just my opinion. It's your policy about Issues section, which I _personally_ think that it is too hard.
@hkcorac I'd like to encourage people to use gitter for questions and leave issues with bugs/enhancements
We may need to add example of various client usages in examples repo though
@crusty-dave I guess you might find the answer above. In my opinion, reddit is not a good place to ask for questions about coding snippets. Next time you might want to go straight to the project repository (and check the README file first).
My repository, before I dropped actix-web client might of use as example for client only code https://github.com/DoumanAsh/fie/tree/f6b6ef530a1594df242306451d82f637e5bd2b5d
Thanks @DoumanAsh , I understand. Your answer did help me too.
Actix is a good place to start learning about futures IMO,
For reference:
https://github.com/actix/examples/blob/master/http-proxy/src/main.rs
E.g.
use futures::future::lazy;
use actix_rt::System;
use actix_web::client::Client;
fn main() {
System::new("test").block_on(lazy(|| {
let client = Client::default();
client.get("https://www.rust-lang.org") // <- Create request builder
.header("User-Agent", "Actix-web")
.send() // <- Send http request
.map_err(|_| ())
.and_then(|mut response| { // <- server http response
println!("Response: {:?}", response);
response.body().and_then(move |bytes| {
let s = std::str::from_utf8(&bytes).expect("utf8 parse error)");
println!("html: {:?}", s);
// Ok(())
// Or return something else...
Ok(HttpResponse::Ok()
.content_type("text/html")
.body(format!("{}", s)))
})
.map_err(|_| ()) // <- Handle error properly
// .wait() // blocking...
})
}));
}
This worked for me:
let mut res = client
.get("http://example.com")
.await?;
let body = res.body().await?; // Bytes
let utf8 = std::str::from_utf8(body.as_ref()).unwrap(); // .as_ref converts Bytes to [u8]
I believe it only works in my case because .await? (from res.body().await?) guarantees Bytes is the final value when .as_ref is called. I am a beginner with Rust but that's what I understood searching for a solution.
Im late to the party... But this works for me:
let mut response = client
.get("https://www.url.com/api")
.send()
.await
.unwrap()
.body()
.await;
Not sure if this is the best practice, but is my first time with Rust. And if just in case, the toml has to be:
actix-web = {version="2.0.0", features=["openssl"]}
openssl = "0.10.29"
If you are sending the request to a secure url
Most helpful comment
Im late to the party... But this works for me:
Not sure if this is the best practice, but is my first time with Rust. And if just in case, the toml has to be:
If you are sending the request to a secure url