
Rust 生态中的 reqwest 和 serde 两个 crate,对使用 Rust 语言进行各类开放 web API 调用提供了强力支持。我们使用 Rust 语言及其相关 crate,对 github web api 进行调用,通过从查询 GitHub API、检查 API 资源是否存在、使用 GitHub API 创建和删除 Gist、使用 RESTful API 分页、处理速率受限 API 等几个实例来做一个展示。

使用 RESTful API 分页
以将分页的 web API 方便地包裹在 Rust 迭代器中,当到达每一页的末尾时,迭代器会从远程服务器加载下一页结果。
use reqwest::Result;
use serde::Deserialize;
#[derive(Deserialize)]
struct ApiResponse {
dependencies: Vec<Dependency>,
meta: Meta,
}
#[derive(Deserialize)]
struct Dependency {
crate_id: String,
}
#[derive(Deserialize)]
struct Meta {
total: u32,
}
struct ReverseDependencies {
crate_id: String,
dependencies: <Vec<Dependency> as IntoIterator>::IntoIter,
client: reqwest::blocking::Client,
page: u32,
per_page: u32,
total: u32,
}
impl ReverseDependencies {
fn of(crate_id: &str) -> Result<Self> {
Ok(ReverseDependencies {
crate_id: crate_id.to_owned(),
dependencies: vec![].into_iter(),
client: reqwest::blocking::Client::new(),
page: 0,
per_page: 100,
total: 0,
})
}
fn try_next(&mut self) -> Result<Option<Dependency>> {
if let Some(dep) = self.dependencies.next() {
return Ok(Some(dep));
}
if self.page > 0 && self.page * self.per_page >= self.total {
return Ok(None);
}
self.page += 1;
let url = format!("https://crates.io/api/v1/crates/{}/reverse_dependencies?page={}&per_page={}",
self.crate_id,
self.page,
self.per_page);
let response = self.client.get(&url).send()?.json::<ApiResponse>()?;
self.dependencies = response.dependencies.into_iter();
self.total = response.meta.total;
Ok(self.dependencies.next())
}
}
impl Iterator for ReverseDependencies {
type Item = Result<Dependency>;
fn next(&mut self) -> Option<Self::Item> {
match self.try_next() {
Ok(Some(dep)) => Some(Ok(dep)),
Ok(None) => None,
Err(err) => Some(Err(err)),
}
}
}
fn main() -> Result<()> {
for dep in ReverseDependencies::of("serde")? {
println!("reverse dependency: {}", dep?.crate_id);
}
Ok(())
}
查询 GitHub API
使用 reqwest::get 查询 点赞的用户 API v3,以获取某个 GitHub 项目的所有点赞用户的列表。使用 Response::json 将响应信息 reqwest::Response 反序列化为实现了 serde::Deserialize trait 的 User 对象。
tokio::main 用于设置异步执行器,该进程异步等待 reqwest::get 完成,然后将响应信息反序列化到用户实例中。
use serde::Deserialize;
use reqwest::Error;
#[derive(Deserialize, Debug)]
struct User {
login: String,
id: u32,
}
#[tokio::main]
async fn main() -> Result<(), Error> {
let request_url = format!("https://api.github.com/repos/{owner}/{repo}/stargazers",
owner = "rust-lang-nursery",
repo = "rust-cookbook");
println!("{}", request_url);
let response = reqwest::get(&request_url).await?;
let users: Vec<User> = response.json().await?;
println!("{:?}", users);
Ok(())
}
检查 API 资源是否存在
使用消息标头 HEAD 请求((Client::head)查询 GitHub 用户端接口,然后检查响应代码以确定是否成功。这是一种无需接收 HTTP 响应消息主体,即可快速查询 rest 资源的方法。使用 ClientBuilder::timeout 方法配置的 reqwest::Client 结构体将确保请求不会超时。
由于 ClientBuilder::build 和 RequestBuilder::send 都返回错误类型 reqwest::Error,所以便捷的 reqwest::Result 类型被用于主函数的返回类型。
use reqwest::Result;
use std::time::Duration;
use reqwest::ClientBuilder;
#[tokio::main]
async fn main() -> Result<()> {
let user = "ferris-the-crab";
let request_url = format!("https://api.github.com/users/{}", user);
println!("{}", request_url);
let timeout = Duration::new(5, 0);
let client = ClientBuilder::new().timeout(timeout).build()?;
let response = client.head(&request_url).send().await?;
if response.status().is_success() {
println!("{} is a user!", user);
} else {
println!("{} is not a user!", user);
}
Ok(())
}
因公众号篇幅和体验限制,使用 GitHub API 创建和删除 Gist、处理速率受限 API 等实例请点击访问 https://rust-cookbook.budshome.com,按照左侧导航阅读。
以上实例代码都是完整的、可独立运行的程序,因此你可以直接复制它们到自己的项目中进行试验。
如果希望从头了解如何运行上述实例代码,请参考《Rust Cookbook 中文版》中关于本书-如何使用本书实例部分。也可以复制链接:http://rust-cookbook.budshome.com/about.html