异步示例很有用,但作为Rust和Tokio的新手,我正在努力研究如何一次执行N个请求,使用向量中的URL,并为每个URL创建响应HTML的迭代器作为字符串.
怎么可以这样做?
这是我的代码。我正在尝试从 Rust subreddit 获取帖子。我将使用 Actix 的内置客户端,但它不适用于 Windows,因此使用reqwest:
use actix_web::{self, middleware, web, App, HttpRequest, HttpServer};
use futures::future::Future;
use reqwest::{
self,
r#async::{Client},
};
fn get_rust_posts(
_req: HttpRequest,
client: web::Data<Client>,
) -> impl Future<Item = String, Error = reqwest::Error> {
client
.get("http://www.reddit.com/r/rust.json")
.send()
.and_then(|mut resp| resp.text())
.map_err(|err| {
println!("Error in get rust posts: {}", err);
err
})
}
fn main() {
let mut server = HttpServer::new(|| {
App::new()
.data(Client::new())
.wrap(middleware::Logger::default())
.service(web::resource("/get/rust/posts").route(web::get().to_async(get_rust_posts)))
});
server.bind(("0.0.0.0", 8000)).unwrap().run().unwrap();
}
Run Code Online (Sandbox Code Playgroud)
[dependencies]
actix-web = "1.0.8"
futures = …Run Code Online (Sandbox Code Playgroud) 通过 reqwest 的请求需要长轮询响应,因此我创建了一个字节流并尝试将每个块反序列化为 JSON。我立即意识到这是错误的,因为每个块都可能不完整,因此反序列化可能会失败;尽管一些块被反序列化为 JSON,但大多数都失败了。我该怎么做呢?
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = reqwest::Client::new();
let mut stream = client
.get("https://...")
.send()
.await?
.bytes_stream();
while let Some(chunk) = stream.next().await {
match serde_json::from_slice::<Value>(&chunk?){
Ok(value) => println!("OK: {:?}", value),
Err(e) => println!("ERROR: {:?}", e),
}
};
Ok(())
}
Run Code Online (Sandbox Code Playgroud) 我对 Rust 完全陌生,我试图找出如何从 URL 端点反序列化任意 JSON 结构。
reqwest README 上的相应示例如下所示:
use std::collections::HashMap;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let resp = reqwest::get("https://httpbin.org/ip")
.await?
.json::<HashMap<String, String>>()
.await?;
println!("{:#?}", resp);
Ok(())
}
Run Code Online (Sandbox Code Playgroud)
所以在这个例子中,目标结构——即一个以字符串作为键和字符串作为值的 HashMap 对象——显然是已知的。
但是,如果我不知道在请求端点上接收到的结构是什么样的呢?
我想使用箱子 A 中的函数返回的类型,该类型实际上是在箱子 B 中定义的,但箱子 A 不会重新导出它。
尽管我可以在我的 中明确添加 crate B Cargo.toml,但我不确定如何使其版本与 crate A 中使用的版本保持同步。
更具体地说,类型是url::ParseError,板条箱 Areqwest和板条箱 B 是url。
我的代码如下所示:
let fetches = futures::stream::iter(
hosts.into_iter().map(|url| {
async move {
match reqwest::get(&url).await {
// Ok and Err statements here!
}
Run Code Online (Sandbox Code Playgroud)
但是,这里的问题是,对于具有无效或自签名 SSL 证书的 URL,它会给出错误。所以,我尝试执行以下操作:
let fetches = futures::stream::iter(
hosts.into_iter().map(|url| {
async move {
match reqwest::Client::builder().danger_accept_invalid_certs(true).build().unwrap().get(&url).await {
// Ok and Err statements here!
}
Run Code Online (Sandbox Code Playgroud)
当我尝试使用 Cargo 构建它时,它显示“错误[E0277]:`RequestBuilder`不是未来”。
那么,如何让我的代码接受无效证书呢?
我在用着reqwest = { version = "0.11", features = ["json"] }
impl Client {
pub fn new(/*endpoint: Url*/) -> Result<Client> {
Ok(Client {
client: reqwest::ClientBuilder::new().build()?,
})
}
}
Run Code Online (Sandbox Code Playgroud)
impl Client {
pub fn new(/*endpoint: Url*/) -> Result<Client> {
Ok(Client {
client: reqwest::ClientBuilder::new().build()?,
})
}
}
Run Code Online (Sandbox Code Playgroud)
我无法设置基本授权标头,并且代码给出错误“缺少身份验证凭据”。
我正在尝试反序列化以下 API 响应(为简单起见,我将仅复制数组的两个切片,但实际上它会更大)。为了演示该示例,代码过于简单。
API响应:
[[1609632000000,32185,32968,34873,31975,18908.90248876],[1609545600000,29349.83250154,32183,33292,29000,22012.92431526]]
所以它是一个大数组/向量,由具有六个整数或浮点数的数组/向量组成(它们的位置也会变化)。
为此,我尝试使用泛型,但似乎我丢失了一些东西,因为我无法编译它。
它失败了
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: reqwest::Error { kind: Decode, source: Error("invalid type: integer `1609632000000`, expected struct T", line: 1, column: 15) }'
use blocking::Response;
use serde::{Deserialize, Serialize};
use reqwest::{blocking, Error, StatusCode};
struct allData<T> {
data: Slice<T>
}
#[derive(Debug, Serialize, Deserialize)]
struct Slice<T>{
data1: T,
data2: T,
data3: T,
data4: T,
data5: T,
data6: T,
}
fn get_data() -> Option<Slice> /*I know the signature is not correct, read …Run Code Online (Sandbox Code Playgroud) 我正在使用 actix-web 构建一个 Web 服务器,其中一种方法使用 reqwest 向外部 API 发出 HTTP 请求:
#[get("/foo")]
async fn foo() -> impl Responder {
let resp = reqwest::get("https://externalapi.com/bar").await?; # GET request inside server
...
}
Run Code Online (Sandbox Code Playgroud)
为了提高性能,我想重用 reqwest 的客户端,因为根据doc ,它拥有一个连接池。但是,我无法使用Arc共享客户端,因为文档还有以下声明:
您不必将 Client 包装在 Rc 或 Arc 中即可重用它,因为它已经在内部使用了 Arc。
如何在函数调用之间共享客户端?或者,我应该使用不同的库在 Web 服务器内创建 HTTP 请求吗?
我正在将 a 解码reqwest::Response为 JSON。通常这工作正常,但在极少数情况下,远程服务器返回的响应不适合我struct用于反序列化的响应。在这些情况下,我想打印出原始响应文本以进行进一步调试。
但是,我在执行 JSON 反序列化和打印响应正文时遇到了麻烦。我想做的是
#[derive(serde::Deserialize)]
struct MyData {
// ...
}
async fn get_json(url: &str) -> Result<MyData, reqwest::Error> {
let response = reqwest::get(url).await?;
let text = response.text().await?;
response
.json::<MyData>().await
.map_err(|err| {
println!(
"Could not decode response from {}: {}", url, text
);
err
})
}
Run Code Online (Sandbox Code Playgroud)
但这不起作用,因为response.text需要self,所以我不能重复使用responsefor response.json。
基于另一个答案的代码(也在这个答案中推荐)我发现了这种方法:
let response = reqwest::get(url).await?;
let text = response.text().await?;
serde_json::from_str(&text).map_err(...)
Run Code Online (Sandbox Code Playgroud)
但是,serde_json::from_str返回 a …
reqwest ×10
rust ×9
serde ×3
json ×2
rust-tokio ×2
actix-web ×1
header ×1
http ×1
ownership ×1
rust-actix ×1
rust-cargo ×1
traits ×1
web ×1