如何将hyper的Body流转换为Result <Vec <String >>?

时间:2020-07-20 10:07:13

标签: rust future hyper

我正在将代码更新为超级和期货的最新版本,但是我尝试过的所有内容都错过了某种或多种实现的特征。

对此不起作用的示例playground ...

extern crate futures; // 0.3.5
extern crate hyper; // 0.13.6

use futures::{future, FutureExt, StreamExt, TryFutureExt, TryStreamExt};
use hyper::body;

fn get_body_as_vec<'a>(b: body::Body) -> future::BoxFuture<'a, Result<Vec<String>, hyper::Error>> {
    let f = b.and_then(|bytes| {
        let s = std::str::from_utf8(&bytes).expect("sends no utf-8");
        let mut lines: Vec<String> = Vec::new();
        for l in s.lines() {
            lines.push(l.to_string());
        }
        future::ok(lines)
    });

    Box::pin(f)
}

这会产生错误:

error[E0277]: the trait bound `futures::stream::AndThen<hyper::Body, futures::future::Ready<std::result::Result<std::vec::Vec<std::string::String>, hyper::Error>>, [closure@src/lib.rs:8:24: 15:6]>: futures::Future` is not satisfied
  --> src/lib.rs:17:5
   |
17 |     Box::pin(f)
   |     ^^^^^^^^^^^ the trait `futures::Future` is not implemented for `futures::stream::AndThen<hyper::Body, futures::future::Ready<std::result::Result<std::vec::Vec<std::string::String>, hyper::Error>>, [closure@src/lib.rs:8:24: 15:6]>`
   |
   = note: required for the cast to the object type `dyn futures::Future<Output = std::result::Result<std::vec::Vec<std::string::String>, hyper::Error>> + std::marker::Send`

我无法创建兼容的未来。 Body是一个流,我找不到实现所需特征的任何“转换器”功能。

对于超级0.12,我使用了concat2()

2 个答案:

答案 0 :(得分:2)

来自reference of and_then

请注意,此函数消耗接收流并返回一个 包装后的版本。

处理整个流并返回单个的Future表示 成功或错误,请改用href=#id

是的,您的try_for_each仍然是流,f可以作为建议的参考,但try_for_each是将字节表示为矢量中的行,但最好是@Shepmaster points in the comment;的更好选择如果我们直接将块转换为UTF-8,则可能会丢失响应中多字节字符的完整性。

由于数据的一致性,最简单的解决方案可能是在转换为UTF-8之前收集所有字节。

try_fold

Playground


您可以使用hyper Body中的use futures::{future, FutureExt, TryStreamExt}; use hyper::body; fn get_body_as_vec<'a>(b: body::Body) -> future::BoxFuture<'a, Result<Vec<String>>> { let f = b .try_fold(vec![], |mut vec, bytes| { vec.extend_from_slice(&bytes); future::ok(vec) }) .map(|x| { Ok(std::str::from_utf8(&x?)? .lines() .map(ToString::to_string) .collect()) }); Box::pin(f) } 测试多块行为。这是我在大块场景中创建的行分区,它可以与上面的代码配合使用,但是如果直接处理大块,则会失去一致性。

channel
  • Playground(成功方案)
  • Playground(失败情况:“第5行的下一个字节”将是 在let (mut sender, body) = body::Body::channel(); tokio::spawn(async move { sender .send_data("Line1\nLine2\nLine3\nLine4\nLine5".into()) .await; sender .send_data("next bytes of Line5\nLine6\nLine7\nLine8\n----".into()) .await; }); println!("{:?}", get_body_as_vec(body).await); 中表示为新行)

注意::由于Vecstd::error:Error都实现了{{1} } hyper::Error策略。

答案 1 :(得分:0)

我找到了两个解决方案,每个解决方案都很简单:

/*
    WARNING for beginners!!! This use statement
    is important so we can later use .data() method!!!
*/
use hyper::body::{to_bytes, HttpBody};

// Takes only single chunk of data!
let my_vector: Vec<u8> = request.into_body().data().await.unwrap().unwrap().to_vec();

// Takes all data chunks, not just the first one:
let my_bytest = body::to_bytes(res.into_body()).await?;

let my_string = String::from_utf8(my_vector).unwrap();

此示例无法正确处理错误,请确保您的代码可以正确处理。