Rust 每日一库之 reqwest
今天给大家推荐一个 crate 的使用:reqwest,主要介绍如何使用 reqwest crate 与 Rust 中的 HTTP API 交互。从通过序列化、代理和自定义头的简单 HTTP 请求开始。
作为 Rust 之旅的一部分,我正在构建一个简单的 CLI 来解决我必须定期完成的手动任务。其中一些任务可以通过利用 Rust 中的 HTTP 客户端来自动化。一项快速的研究让我找到了reqwest[1],这是一个简单的 Rust HTTP 客户端,满足我的需求,让我快速完成工作。
你可能会问 reqwest
是不是 Rust 的 最佳 HTTP 客户端? 老实说,我不知道。但是我用它很快就完成了我的工作,而且我真的很喜欢reqwest
。让我们深入了解 reqwest
。
01 安装 reqwest
你可以通过将它加入 Cargo.toml
这种方式简单快速安装 reqwest
,同时把 tokio
也添加并安装,因为 reqwest 底层使用了这个异步运行时。
[dependencies]
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1", features = ["full"] }
虽然reqwest
可以使用不同的Content-Types
,但这篇文章侧重于处理 JSON,因为我处理的大多数 HTTP API 都以这种特定格式返回数据。
02 使用 reqwest 的简单 GET 请求
让我们发出一个简单的 GET 请求来查看 HTTP 调用的reqwest
实际情况。
let b = reqwest::get("https://swapi.dev/api/people")
.await?
.json()
.await?;
println!("Got {:?}", b);
本示例使用简便的方法 get
快速发出简单的 HTTP GET 请求。很有可能,你会在应用程序中发出许多不同的请求。如果是这种情况,你应该考虑创建一个专用的Client
并将其重用于多个独立的 HTTP 请求。
03 reqwest 中的 Client 和 RequestBuilder
通过使用专用Client
,你可以快速创建不同类型的新请求。Client
提供诸如 get
, post
, put
, delete
... 之类的方法,以及 request(&self, method: Method, url: U)
可用于创建新RequestBuilder
。RequestBuilder
在发出某个请求之前,你可以自定义它的所有方面。RequestBuilder
提供的功能被设计为链式(或流畅的)API。例如,考虑以下 GET 请求,它在发出请求之前添加额外的 HTTP 头并指定自定义超时:
use reqwest;
use std::error::Error;
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let client = reqwest::Client::new();
let doge = client
.get("https://api.coinstats.app/public/v1/coins/dogecoin")
.header("Accept", "text/plain")
.timeout(Duration::from_secs(3))
.send()
.await?
.text()
.await?;
println!("{:}", doge);
Ok(())
}
04 默认 HTTP 头
在前面的示例中,RequestBuilder
使用 header
函数设置 HTTP 头:Accept
。你还可以使用ClientBuilder
为发出的所有请求指定 HTTP 头:
use reqwest;
use reqwest::header;
use std::error::Error;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let mut h = header::HeaderMap::new();
h.insert("Accept", header::HeaderValue::from_static("application/json"));
let client = reqwest::Client::builder()
.default_headers(h)
.build()?;
let doge = client
.get("https://api.coinstats.app/public/v1/coins/dogecoin")
.send()
.await?
.text()
.await?;
println!("{:}", doge);
Ok(())
}
05 使用 reqwest 解压 GZIP 响应
当 API 返回的数据使用了 GZIP 压缩时,你可以即时解压缩。这需要 reqwest
启用 gzip
功能(在 Cargo.toml
中配置)。GZIP
通过调用以下 gzip
函数启用自动解压缩ClientBuilder
:
// omitted
let client = reqwest::Client::builder()
.gzip(true)
.default_headers(h)
.build()?;
// omitted
如果服务器将 Content-Encoding: gzip
HTTP 头作为 HTTP 响应的一部分发送,则响应正文将自动解压缩。
06 反序列化 JSON 响应正文
要将响应反序列化为 struct
,请将 serde
添加到 Cargo.toml
:
[dependencies]
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
显然,你必须在 Rust 中重新创建响应结构,并使用 Response.json()
。你可以显式提供所需变量的类型 ( let p: Response = r::json().await?;
) ,或者使用 turbo-fish 语法 ( let p = r.json::
) 调用 json()
,如下所示:
use std::error::Error;
use serde::Deserialize;
#[derive(Deserialize, Debug)]
struct Response {
coins: Vec,
}
#[derive(Deserialize, Debug)]
struct Coin {
id: String,
name: String,
icon: String,
symbol: String,
price: f32,
priceBtc: f32,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let http_response = reqwest::get("https://api.coinstats.app/public/v1/coins?skip=0&limit=10").await?;
let response = http_response.json::().await?;
println!("{:#?}", response.coins);
Ok(())
}
07 使用 reqwest 的自定义 HTTP 代理
reqwest
默认情况下可以使用 HTTP 代理。如果设置,HTTP 代理 URL 将自动从 HTTP_PROXY
和 HTTPS_PROXY
环境变量加载。你还可以使用 Proxy
如下所示的结构覆盖这些设置:
// omitted
let client = reqwest::Client::builder()
.proxy(reqwest::Proxy::https("https://my-proxy.local")
.build()?;
// omitted
你可以在 ClientBuilder
上调用 no_proxy()
来禁用代理:
// omitted
let client = reqwest::Client::builder()
.no_proxy()
.build()?;
// omitted
08 结论
使用 reqwest
,我可以轻松地与 HTTP API 交互。它只是另一个 HTTP 客户端库,这里没什么特别的 。它满足我的要求。我喜欢流畅的 API 设计,它可以让我快速调用给定的 HTTP(s) API。也就是说,reqwest
值得一看。
参考资料
reqwest: https://github.com/seanmonstar/reqwest
推荐阅读
我是 polarisxu,北大硕士毕业,曾在 360 等知名互联网公司工作,10多年技术研发与架构经验!2012 年接触 Go 语言并创建了 Go 语言中文网!著有《Go语言编程之旅》、开源图书《Go语言标准库》等。
坚持输出技术(包括 Go、Rust 等技术)、职场心得和创业感悟!欢迎关注「polarisxu」一起成长!也欢迎加我微信好友交流:gopherstudio