3
votes

Quelle est la bonne façon d'extraire le corps d'une réponse actix_web dans une chaîne?

J'essaie d'utiliser actix_web pour récupérer et afficher le contenu d'une page Web. La requête HTTP se termine avec succès et je peux afficher la page Web, mais je veux lire le corps dans une String pour l'impression.

J'ai essayé let my_ip: String = response.body () .into (); mais j'obtiens une erreur qui dit

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

Voici ce que j'ai jusqu'à présent:

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(())
            })
    });
}


2 commentaires

@Shepmaster Vous avez raison, je suis depuis si longtemps pour comprendre que ma question laissait beaucoup à désirer. J'ai parcouru et clarifié les choses et je me suis assuré que le message d'erreur correspondait exactement à l'exemple.


Pas de soucis; c'est super facile de se retrouver pris dans un problème sur lequel nous travaillons depuis un moment et de perdre de vue la vue d'ensemble!


3 Réponses :


4
votes

Afin de conserver l'objet response que vous avez tout en extrayant le corps, nous allons profiter du fait que, contrairement à quelques autres frameworks, vous pouvez extraire le corps sans déstructurer l'objet entier. Le code est ci-dessous:

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

Dans l'ordre:

  • tout ce qui s'y trouve utilise désormais std :: io :: Error pour plus de facilité d'utilisation. Comme tous les types d'erreur actix implémentent Error , il est également possible de conserver les types d'origine
  • and_then () me permet d'extraire le corps. Lorsque cela est résolu, une map (avec move s'assurant que nous prenons response avec nous) puis renvoie un tuple de ( réponse, corps)
  • À partir de là, vous pouvez utiliser librement la réponse ou le corps comme bon vous semble

Notez que j'ai remplacé votre nom d'hôte par localhost à des fins de test, car ipv4.canhasip.com ne résout actuellement rien de l'extérieur.


Réponse initiale:

Vous auriez vraiment dû fournir plus de contexte sur celui-ci. actix a plusieurs types de requête.

Votre objet initial ( réponse ) est un ClientResponse . appeler body () dessus renvoie une structure MessageBody , qui est le début du terrier dans lequel vous êtes tombé. Ce n'est PAS le corps réel, simplement un objet implémentant le trait Future et qui, une fois qu'il aura suivi son cours, donnera ce que vous recherchez.

Vous devrez le faire de manière moins hacky, mais pour l'instant et pour vous convaincre que c'est la source du problème, au lieu de votre ligne de code, essayez ceci:

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(|_| ())
});


2 commentaires

Désolé pour le manque de détails. Je ne savais pas qu'il y avait plusieurs types de demandes. J'ai édité la question avec un exemple. Voyez si cela aide.


La réponse de @BonsaiOak est retardée, le travail est ... inhabituellement intéressant . Modifier la réponse pour en fournir un peu plus.



1
votes

Pour compléter la réponse de Sébastien, vous pouvez également résoudre le futur de MessageBody :

.and_then(|response| {
    response.body().map_err(|_| ()).and_then(|bytes| {
        println!("{:?}", bytes);
        Ok(())
    })
})


2 commentaires

Je ne voulais pas aller aussi loin car, en raison de la brièveté de l'exemple de l'OP, il n'est pas garanti qu'il ne retourne pas déjà quelque chose implémentant IntoFuture de où il a obtenu cet objet, d'où la mise en garde :-)


J'ai édité ma question avec un exemple. Voyez si cela aide.



1
votes
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
});
The result of send is a SendRequest. This is a future that resolves into a ClientResponse. ClientResponse implements HttpMessage, which has the method
HttpMessage::body. This returns a future that resolves into a Bytes. This can be converted into a String through the usual Rust methods.See also:
How do I read the entire body of a Tokio-based Hyper request?
How do I convert a Vector of bytes (u8) to a string

2 commentaires

Quelle est la signification de drop dans map_err ?


On dirait que drop est une fonction prélude transmise à map_err qui supprime l'erreur. doc.rust-lang.org/std/ops/trait.Drop. html doc.rust-lang.org/std/prelude