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
