调用 Web API
查询 GitHub API
reqwest::get
查询 Github 的stargazers API v3,获取星号标记项目的所有用户的列表。reqwest::Response
用Response::json
反序列化为User
对象,而这对象实现了serde::Deserialize
。
#[macro_use] extern crate serde_derive; extern crate reqwest; use reqwest::Error; #[derive(Deserialize, Debug)] struct User { login: String, id: u32, } 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 mut response = reqwest::get(&request_url)?; let users: Vec<User> = response.json()?; println!("{:?}", users); Ok(()) }
检查一个 API 资源是否存在
使用标头请求(Client::head
),查询 GitHub 用户端点,然后检查响应代码,以确定是否成功。这是一种无需接收主体,即可快速查询 REST 资源的方法。reqwest::Client
与ClientBuilder::timeout
合作,确保请求的持续时间不会超时。
extern crate reqwest; use reqwest::Error; use std::time::Duration; use reqwest::ClientBuilder; fn main() -> Result<(), Error> { 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()?; if response.status().is_success() { println!("{} is a user!", user); } else { println!("{} is not a user!", user); } Ok(()) }
使用 GitHub API ,创建和删除 Gist
向 Github gists API v3 发出 POST 请求,创建一个 Gist ,由Client::post
完成,并使用Client::delete
DELETE 请求,将其删除。
这个reqwest::Client
负责两个请求的详细信息,包括 URL、主体和身份验证。POST 主体来自serde_json::json!
宏,它能提供任意 JSON 主体。调用RequestBuilder::json
设置请求正文。RequestBuilder::basic_auth
就处理身份验证。同步调用RequestBuilder::send
执行请求。
# #[macro_use] # extern crate error_chain; extern crate reqwest; #[macro_use] extern crate serde_derive; #[macro_use] extern crate serde_json; use std::env; use reqwest::Client; # # error_chain! { # foreign_links { # EnvVar(env::VarError); # HttpRequest(reqwest::Error); # } # } #[derive(Deserialize, Debug)] struct Gist { id: String, html_url: String, } fn run() -> Result<()> { let gh_user = env::var("GH_USER")?; let gh_pass = env::var("GH_PASS")?; let gist_body = json!({ "description": "the description for this gist", "public": true, "files": { "main.rs": { "content": r#"fn main() { println!("hello world!");}"# } }}); let request_url = "https://api.github.com/gists"; let mut response = Client::new() .post(request_url) .basic_auth(gh_user.clone(), Some(gh_pass.clone())) .json(&gist_body) .send()?; let gist: Gist = response.json()?; println!("Created {:?}", gist); let request_url = format!("{}/{}",request_url, gist.id); let response = Client::new() .delete(&request_url) .basic_auth(gh_user, Some(gh_pass)) .send()?; println!("Gist {} deleted! Status code: {}",gist.id, response.status()); Ok(()) } # # quick_main!(run);
示例使用HTTP 基本 身份验证,这是为了获取GitHub API授权访问。典型的用例将使用一个更复杂的OAuth授权流程。
消耗一个分页的 RESTful API
在方便的 Rust 迭代器中,包装一个分页的 Web API。迭代器在到达每一页的末尾时,从远程服务器惰性地获取下一页的结果。
#[macro_use] extern crate serde_derive; extern crate reqwest; use reqwest::Error; #[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::Client, page: u32, per_page: u32, total: u32, } impl ReverseDependencies { fn of(crate_id: &str) -> Result<Self, Error> { Ok(ReverseDependencies { crate_id: crate_id.to_owned(), dependencies: vec![].into_iter(), client: reqwest::Client::new(), page: 0, per_page: 100, total: 0, }) } fn try_next(&mut self) -> Result<Option<Dependency>, Error> { 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, Error>; 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<(), Error> { for dep in ReverseDependencies::of("serde")? { println!("reverse dependency: {}", dep?.crate_id); } Ok(()) }