Chetan Reddy Kodidela 2025-04-14
Reqwest 是 Rust 语言中一个功能强大的 HTTP 客户端,支持异步(asynchronous)和阻塞(blocking)两种调用方式。根据你的应用程序需求——无论是发起单个 HTTP 请求,还是处理多个并发请求——你都可以在这两种模式之间进行选择。
在本文中,我们将涵盖以下内容:
- Reqwest 简介
- 同步(阻塞)API 的使用
- 结合 Tokio 和 Serde 的异步 API 使用
- 发起 GET 和 POST 请求
- 处理 JSON 的序列化与反序列化
- Cargo.toml 中的依赖项与配置
1. Reqwest 简介
Reqwest 基于 hyper 构建,提供了用于处理 HTTP 请求和响应的高级 API。它支持以下特性:
- 连接池:通过单一客户端复用多个请求的连接。
- 自动重定向处理。
- 通过与 Serde 集成实现 JSON 的(反)序列化。
- 提供阻塞和异步两种风格,以适应不同的使用场景。
2. 同步(阻塞)API 的使用
阻塞 API 非常适合简单的脚本,或者当你只需要执行一次 API 调用、且不希望引入异步编程复杂性时。你可以通过启用 "blocking" 特性来使用它。
示例:使用阻塞客户端发起基本的 GET 请求
use reqwest::blocking::{Client, ClientBuilder};
use reqwest::redirect::Policy;
fn main() {
// 创建一个基本的 HTTP 客户端实例。
let http_client = Client::new();
// 发起一个简单的 GET 请求。
let http_result = http_client
.get("https://jsonplaceholder.typicode.com/posts/1")
.send();
// 检查请求是否成功,然后打印响应体。
if let Ok(response) = http_result {
// 在生产代码中,建议对 .text() 的潜在错误进行处理。
match response.text() {
Ok(text) => println!("Body: {:#?}", text),
Err(e) => eprintln!("Failed to read response text: {:?}", e),
}
} else if let Err(e) = http_result {
eprintln!("Error: {:?}", e);
}
// 演示使用限制策略处理重定向。
let redir_policy = Policy::limited(5);
let http_client = ClientBuilder::new()
.redirect(redir_policy)
.build()
.expect("ClientBuilder failed to build a client");
// 此调用最多跟随 5 次重定向。
let http_result2 = http_client
.get("http://httpbin.org/redirect-to?url=https://example.com")
.send()
.expect("Failed to send redirected request");
println!("Response with redirection: {:#?}", http_result2);
}
关键点:
- 阻塞调用:代码会等待请求完成后再执行下一行。
- 错误处理:示例中包含了基本的错误处理,说明如何检查错误。
- 重定向策略:你可以使用
Policy::limited自定义重定向行为。
3. 异步 API 的使用
对于多个或长时间运行的 HTTP 调用,异步 API(由 Tokio 提供支持)允许你的应用程序在等待响应的同时执行其他任务。这在服务器或需要并发处理大量 HTTP 请求的应用程序中特别有用。
示例 1:带 JSON 反序列化的异步 GET 请求
简单的 GET 示例
// 将 Tokio 运行时引入作用域。
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let url = "https://www.rust-lang.org";
// 异步 GET 请求。
let response = reqwest::get(url).await?;
// 提取并打印响应体。
let response_body = response.text().await?;
println!("Response Body: {:?}", response_body);
Ok(())
}
将 JSON 响应反序列化为通用值(Generic Value)
use serde_json::Value;
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let posts_url = "https://jsonplaceholder.typicode.com/posts";
// 异步 GET 请求。
let resp = reqwest::get(posts_url).await?;
// 将 JSON 响应转换为 serde_json::Value。
let posts: Value = resp.json().await?;
dbg!(posts);
Ok(())
}
示例 2:将 JSON 反序列化为 Rust 结构体
为了获得更强的类型安全性和代码清晰度,通常会定义一个与 JSON 数据对应的结构体。
use serde::Deserialize;
use reqwest;
use tokio;
#[derive(Debug, Deserialize)]
struct Post {
userId: i32,
id: i32,
title: String,
body: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let posts_url = "https://jsonplaceholder.typicode.com/posts";
// 异步发起 GET 请求。
let response = reqwest::get(posts_url).await?;
// 将 JSON 响应反序列化为 Post 结构体的向量。
let posts: Vec<Post> = response.json().await?;
// 遍历并打印每篇帖子的 ID 和标题。
for post in posts {
println!("ID: {}, Title: {}", post.id, post.title);
}
Ok(())
}
示例 3:将数据获取到 HashMap 中
有时响应数据无法很好地映射到预定义的结构体中——此时使用 HashMap 是一种灵活的选择。
use std::collections::HashMap;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let resp = reqwest::get("https://httpbin.org/ip")
.await?
.json::<HashMap<String, String>>()
.await?;
println!("{:#?}", resp);
Ok(())
}
关键点:
- 异步执行:
#[tokio::main]属性会创建一个运行时来执行异步代码。 - 错误处理:使用
?操作符传播错误。在生产代码中,可能需要更细粒度的错误处理。 - 反序列化:使用 Serde 将 JSON 响应转换为 Rust 类型,提高类型安全性和代码可读性。
4. 发起 POST 请求
除了 GET 请求,Reqwest 也轻松支持 POST 请求。以下是一个发送纯文本请求体的示例。
use reqwest::Error;
#[tokio::main]
async fn main() -> Result<(), Error> {
let client = reqwest::Client::new();
// 发送带有请求体的 POST 请求。
let res = client
.post("http://httpbin.org/post")
.body("the exact body that is sent")
.send()
.await?;
// 打印响应体。
let body = res.text().await?;
println!("Response: {}", body);
Ok(())
}
POST 请求的关键注意事项:
- 请求体格式:Reqwest 支持多种请求体格式。根据你的 API,你可能需要发送 JSON、表单数据或其他内容类型。
- 客户端复用:为了执行多个请求,建议复用
Client实例,以便利用连接池提升性能。
5. 配置依赖项
要在项目中使用 Reqwest,请在 Cargo.toml 中添加相应的依赖项。以下是异步和阻塞示例的配置。
用于异步代码
[dependencies]
reqwest = { version = "0.10", features = ["json"] }
tokio = { version = "0.2", features = ["full"] }
serde = { version = "1.0", features = ["derive"] } # 可选,当使用结构体进行 JSON 反序列化时需要
用于阻塞代码
[dependencies]
reqwest = { version = "0.10", features = ["blocking", "json"] }
serde = { version = "1.0", features = ["derive"] } # 可选,当使用结构体进行 JSON 反序列化时需要
小技巧:你可以通过命令行快速添加 reqwest:
cargo add reqwest此命令会确保你使用的是 reqwest 0.10.10 版本,并启用适当的特性。
6. 阻塞 vs 异步:何时使用哪种?
阻塞(同步)模式:
适用于简单、单一或不频繁的请求场景,此时应用程序不需要处理多个并发网络操作。这种模式编写起来更简单,如果你的用例很直接,也更容易理解。
异步(非阻塞)模式:
当你的应用程序需要并发执行多个 HTTP 请求时(例如网络爬虫、处理多个客户端请求的服务器),异步编程可以让你最高效地利用系统资源。它避免了在每个请求完成前等待,从而实现更高的吞吐量和响应能力。
结论
Reqwest 是一个用途广泛的 Rust HTTP 客户端,既适用于简单的脚本,也能胜任复杂的异步应用程序。你选择阻塞还是异步操作,取决于具体的使用场景。本文提供的示例展示了如何:
- 发起 GET 和 POST 请求,
- 处理重定向,
- 使用 Serde 将 JSON 响应反序列化为 Rust 类型。
欢迎你根据这些示例和最佳实践,构建能够高效与 HTTP API 通信的健壮应用程序。