Rust 每日一庫之 reqwest
今天給大家推薦一個 crate 的使用:reqwest,主要介紹如何使用 reqwest crate 與 Rust 中的 HTTP API 交互。從通過序列化、代理和自定義頭的簡單 HTTP 請求開始。
作為 Rust 之旅的一部分,我正在構(gòu)建一個簡單的 CLI 來解決我必須定期完成的手動任務(wù)。其中一些任務(wù)可以通過利用 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,但這篇文章側(cè)重于處理 JSON,因為我處理的大多數(shù) HTTP API 都以這種特定格式返回數(shù)據(jù)。
02 使用 reqwest 的簡單 GET 請求
讓我們發(fā)出一個簡單的 GET 請求來查看 HTTP 調(diào)用的reqwest實際情況。
let?b?=?reqwest::get("https://swapi.dev/api/people")
????.await?
????.json()
????.await?;
println!("Got?{:?}",?b);
本示例使用簡便的方法 get 快速發(fā)出簡單的 HTTP GET 請求。很有可能,你會在應(yīng)用程序中發(fā)出許多不同的請求。如果是這種情況,你應(yīng)該考慮創(chuàng)建一個專用的Client并將其重用于多個獨立的 HTTP 請求。
03 reqwest 中的 Client 和 RequestBuilder
通過使用專用Client,你可以快速創(chuàng)建不同類型的新請求。Client提供諸如 get, post, put, delete ... 之類的方法,以及 request(&self, method: Method, url: U) 可用于創(chuàng)建新RequestBuilder。RequestBuilder 在發(fā)出某個請求之前,你可以自定義它的所有方面。RequestBuilder 提供的功能被設(shè)計為鏈?zhǔn)剑ɑ蛄鲿车模〢PI。例如,考慮以下 GET 請求,它在發(fā)出請求之前添加額外的 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 默認(rèn) HTTP 頭
在前面的示例中,RequestBuilder 使用 header 函數(shù)設(shè)置 HTTP 頭:Accept。你還可以使用ClientBuilder為發(fā)出的所有請求指定 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 響應(yīng)
當(dāng) API 返回的數(shù)據(jù)使用了 GZIP 壓縮時,你可以即時解壓縮。這需要 reqwest 啟用 gzip 功能(在 Cargo.toml 中配置)。GZIP 通過調(diào)用以下 gzip 函數(shù)啟用自動解壓縮ClientBuilder:
//?omitted
let?client?=?reqwest::Client::builder()
????.gzip(true)
????.default_headers(h)
????.build()?;
//?omitted
如果服務(wù)器將 Content-Encoding: gzip HTTP 頭作為 HTTP 響應(yīng)的一部分發(fā)送,則響應(yīng)正文將自動解壓縮。
06 反序列化 JSON 響應(yīng)正文
要將響應(yīng)反序列化為 struct,請將 serde 添加到 Cargo.toml:
[dependencies]
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
顯然,你必須在 Rust 中重新創(chuàng)建響應(yīng)結(jié)構(gòu),并使用 Response.json() 。你可以顯式提供所需變量的類型 ( let p: Response = r::json().await?;) ,或者使用 turbo-fish 語法 ( let p = r.json::) 調(diào)用 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 默認(rèn)情況下可以使用 HTTP 代理。如果設(shè)置,HTTP 代理 URL 將自動從 HTTP_PROXY 和 HTTPS_PROXY 環(huán)境變量加載。你還可以使用 Proxy 如下所示的結(jié)構(gòu)覆蓋這些設(shè)置:
//?omitted
let?client?=?reqwest::Client::builder()
????.proxy(reqwest::Proxy::https("https://my-proxy.local")
????.build()?;
//?omitted你可以在?ClientBuilder?上調(diào)用?no_proxy()?來禁用代理:
//?omitted
let?client?=?reqwest::Client::builder()
????.no_proxy()
????.build()?;
//?omitted
08 結(jié)論
使用 reqwest,我可以輕松地與 HTTP API 交互。它只是另一個 HTTP 客戶端庫,這里沒什么特別的 。它滿足我的要求。我喜歡流暢的 API 設(shè)計,它可以讓我快速調(diào)用給定的 HTTP(s) API。也就是說,reqwest 值得一看。
參考資料
reqwest: https://github.com/seanmonstar/reqwest
推薦閱讀
我是 polarisxu,北大碩士畢業(yè),曾在 360 等知名互聯(lián)網(wǎng)公司工作,10多年技術(shù)研發(fā)與架構(gòu)經(jīng)驗!2012 年接觸 Go 語言并創(chuàng)建了 Go 語言中文網(wǎng)!著有《Go語言編程之旅》、開源圖書《Go語言標(biāo)準(zhǔn)庫》等。
堅持輸出技術(shù)(包括 Go、Rust 等技術(shù))、職場心得和創(chuàng)業(yè)感悟!歡迎關(guān)注「polarisxu」一起成長!也歡迎加我微信好友交流:gopherstudio
