3

我正在尝试使用actix_web来获取和显示网页的内容。HTTP 请求成功完成,我可以查看网页,但我想将正文读入String用于打印。

我试过let my_ip: String = response.body().into();了,但我得到一个错误,上面写着

error[E0277]: the trait bound `std::string::String: std::convert::From<actix_web::httpmessage::MessageBody<actix_web::client::response::ClientResponse>>` is not satisfied
  --> src/main.rs:16:53
   |
16 |                 let my_ip: String = response.body().into();
   |                                                     ^^^^ the trait `std::convert::From<actix_web::httpmessage::MessageBody<actix_web::client::response::ClientResponse>>` is not implemented for `std::string::String`
   |
   = help: the following implementations were found:
             <std::string::String as std::convert::From<&'a str>>
             <std::string::String as std::convert::From<std::borrow::Cow<'a, str>>>
             <std::string::String as std::convert::From<std::boxed::Box<str>>>
             <std::string::String as std::convert::From<trust_dns_proto::error::ProtoError>>
   = note: required because of the requirements on the impl of `std::convert::Into<std::string::String>` for `actix_web::httpmessage::MessageBody<actix_web::client::response::ClientResponse>`

这是我到目前为止所拥有的:

use actix;
use actix_web::{client, HttpMessage};
use futures::future::Future;

fn main() {
    actix::run(|| {
        client::get("http://ipv4.canhasip.com/")
            .header("User-Agent", "Actix-web")
            .finish()
            .unwrap()
            .send()
            .map_err(|_| ())
            .and_then(|response| {
                println!("Response: {:?}", response);
                // error occurs here
                let my_ip: String = response.body().into();
                Ok(())
            })
    });
}

相关依赖版本:

[dependencies]
actix-web = "0.7"
actix = "0.7"
futures = "0.1"
4

3 回答 3

5

为了在response提取主体的同时保留您拥有的对象,我们将利用这一事实,与其他一些框架不同,您可以在不破坏整个对象的情况下提取主体。代码如下:

actix::run(|| {

    client::get("http://localhost/")
        .header("User-Agent", "Actix-web")
        .finish()
        .unwrap()
        .send()
        .map_err(|e| {
          Error::new(ErrorKind::AddrInUse, "Request error", e)
        })
        .and_then(|response| {
          println!("Got response");
          response.body().map(move |body_out| {
            (response, body_out)
          }).map_err(|e| Error::new(ErrorKind::InvalidData, "Payload error", e))
        }).and_then(|(response, body)| {
          println!("Response: {:?}, Body: {:?}", response, body);
          Ok(())
      }).map_err(|_| ())
});

为了:

  • 那里的所有东西现在都std::io::Error用于易用性。由于所有actix错误类型都实现Error了,因此也可以保留原始类型。
  • and_then()允许我提取身体。解决此问题后,a mapmove确保我们随身携带response)然后返回一个元组(response, body)
  • 从那里,您可以自由使用您认为合适的响应或正文

请注意,localhost出于测试目的,我将您的主机名替换为ipv4.canhasip.com当前无法解析外部的任何内容。


初步答案:

你真的应该提供更多关于这个的上下文。actix有多个请求类型。

您的初始对象 ( response) 是ClientResponse. 调用body()它会返回一个MessageBody结构,这是你掉入兔子洞的开始。这不是实际的主体,只是一个实现Futuretrait 的对象,一旦它运行,就会产生你想要的东西。

您需要以一种不那么 hacky 的方式执行此操作,但现在并说服自己这是问题的根源,而不是您的代码行,请尝试以下操作:

println!("{:?}", response.body().wait())

调用wait()未来会阻塞当前线程,这就是为什么我说这是一种临时的、hacky 的方式来告诉你问题出在哪里。根据您可以使用的内容(如果您在某处有类似执行器的对象,或者能够返回执行的未来),您的实际解决方案会有所不同。

于 2019-01-07T13:47:38.377 回答
1

补充塞巴斯蒂安的答案,您还可以解决MessageBody未来:

.and_then(|response| {
    response.body().map_err(|_| ()).and_then(|bytes| {
        println!("{:?}", bytes);
        Ok(())
    })
})
于 2019-01-07T13:51:56.847 回答
1
actix::run(|| {
    client::get("http://ipv4.canhasip.com/")
        .header("User-Agent", "Actix-web")
        .finish()
        .unwrap()
        .send()
        .map_err(drop)
        .and_then(|response| response.body().map_err(drop))
        .map(|body| body.to_vec())
        .map(|body| String::from_utf8(body).unwrap())
        .map(drop) // Do *something* with the string, presumably
});

结果send是一个SendRequest。这是一个解析为ClientResponse. ClientResponseimplements HttpMessage,其中有方法 HttpMessage::body. 这将返回一个解析为Bytes. 这可以String通过通常的 Rust 方法转换为 a。

也可以看看:

于 2019-01-08T02:48:04.067 回答