reqwest 是在 hyper 基础上实现的高层 HTTP client 库,支持异步和同步接口。
request::get()
发送一个 GET 请求,每次请求都创建临时的 Client,效率不高。
// 一次性异步 get 请求
pub async fn get<T: IntoUrl>(url: T) -> Result<Response>
let body = reqwest::get("https://www.rust-lang.org")
.await?
.text() // Response 的 text() 方法
.await?;
println!("body = {body:?}");
建议:创建可复用的 Client 来发送请求,它内部维护 keepalive 连接池。
// 可以复用的 client
let client = reqwest::Client::new();
// Post text body
let res = client.post("http://httpbin.org/post")
.body("the exact body that is sent")
.send()
.await?;
// Post 表单,可以是 tuple array,或 HashMap 或实现 Serialize 的自定义类型。
let params = [("foo", "bar"), ("baz", "quux")];
let client = reqwest::Client::new();
let res = client.post("http://httpbin.org/post")
.form(¶ms)
.send()
.await?;
// Post JSON body, 任何可以被 serialized 为 JSON 的类型值均可。
let mut map = HashMap::new();
map.insert("lang", "rust");
map.insert("body", "json");
let client = reqwest::Client::new();
let res = client.post("http://httpbin.org/post")
.json(&map)
.send()
.await?;
reqwest 默认支持重定向(最多 10 hops),可以通过 ClientBuilder 的 redirect::Policy 来控制。
reqwest 默认支持 Proxy,支持 HTTP/HTTPS/SOCKS5 等代理,支持代理环境变量,可以通过 ClientBuilder 的 Proxy 来控制。
let custom = redirect::Policy::custom(|attempt| {
if attempt.previous().len() > 5 {
attempt.error("too many redirects")
} else if attempt.url().host_str() == Some("example.domain") {
// prevent redirects to 'example.domain'
attempt.stop()
} else {
attempt.follow()
}
});
let client = reqwest::Client::builder()
.redirect(custom)
.build()?;
reqwest 默认支持 TLS,可以添加自定义的 Server/Client 证书。
- 默认开启 default-tls feature,当前对应的是 native-tls;
- 支持的 TLS backend:native-tls(Linux 对应 OpenSSL 实现)、rustls-tls(使用 rustls create);
// Certificate 代表自定义 Server CA 证书,后续可以使用 ClientBuilder::add_root_certificate() 添加
let mut buf = Vec::new();
File::open("my_cert.der")?
.read_to_end(&mut buf)?;
let cert = reqwest::Certificate::from_der(&buf)?;
let mut buf = Vec::new();
File::open("my_cert.pem")?
.read_to_end(&mut buf)?;
let cert = reqwest::Certificate::from_pem(&buf)?;
let mut buf = Vec::new();
File::open("ca-bundle.crt")?
.read_to_end(&mut buf)?;
let certs = reqwest::Certificate::from_pem_bundle(&buf)?;
// Identify 代表 client 证书
let mut buf = Vec::new();
File::open("my-ident.pfx")?
.read_to_end(&mut buf)?;
let pkcs12 = reqwest::Identity::from_pkcs12_der(&buf, "my-privkey-password")?;
// Parses a chain of PEM encoded X509 certificates, with the leaf certificate first. key is a PEM
// encoded PKCS #8 formatted private key for the leaf certificate.
let cert = fs::read("client.pem")?;
let key = fs::read("key.pem")?;
let pkcs8 = reqwest::Identity::from_pkcs8_pem(&cert, &key)?;
// Parses PEM encoded private key and certificate.
let mut buf = Vec::new();
File::open("my-ident.pem")?
.read_to_end(&mut buf)?;
let id = reqwest::Identity::from_pem(&buf)?;
reqwest 默认不启用 cookie store,但是提供了简易的实现类型 Jar:
use reqwest::{cookie::Jar, Url};
let cookie = "foo=bar; Domain=yolo.local";
let url = "https://yolo.local".parse::<Url>().unwrap();
let jar = Jar::default();
jar.add_cookie_str(cookie, &url);
// and now add to a `ClientBuilder`?
1 Url #
Url 用于在构建 Reqeust 时使用。
pub struct Url { /* private fields */ }
impl Url
// 解析 URL 字符串(自动 URL 编码)
pub fn parse(input: &str) -> Result<Url, ParseError>
let url = Url::parse("https://example.net")?;
// 解析 URL 字符串+表单参数(自动 URL 编码)
pub fn parse_with_params<I, K, V>( input: &str, iter: I ) -> Result<Url, ParseError>
where
I: IntoIterator,
<I as IntoIterator>::Item: Borrow<(K, V)>,
K: AsRef<str>,
V: AsRef<str>,
let url = Url::parse_with_params("https://example.net?dont=clobberme", &[("lang", "rust"), ("browser", "servo")])?;
assert_eq!("https://example.net/?dont=clobberme&lang=rust&browser=servo", url.as_str());
// 添加一级 URL Path
pub fn join(&self, input: &str) -> Result<Url, ParseError>
let base = Url::parse("https://example.net/a/b.html")?;
let url = base.join("c.png")?;
assert_eq!(url.as_str(), "https://example.net/a/c.png"); // Not /a/b.html/c.png
let base = Url::parse("https://example.net/a/b/")?;
let url = base.join("c.png")?;
assert_eq!(url.as_str(), "https://example.net/a/b/c.png");
pub fn make_relative(&self, url: &Url) -> Option<String>
let base = Url::parse("https://example.net/a/b/")?;
let url = Url::parse("https://example.net/a/d/c.png")?;
let relative = base.make_relative(&url);
assert_eq!(relative.as_ref().map(|s| s.as_str()), Some("../d/c.png"));
pub fn options<'a>() -> ParseOptions<'a>
pub fn as_str(&self) -> &str
pub fn into_string(self) -> String
pub fn origin(&self) -> Origin
pub fn scheme(&self) -> &str
let url = Url::parse("file:///tmp/foo")?;
assert_eq!(url.scheme(), "file");
pub fn is_special(&self) -> bool
pub fn has_authority(&self) -> bool
let url = Url::parse("ftp://[email protected]")?;
assert!(url.has_authority());
pub fn authority(&self) -> &str
let url = Url::parse("file:///tmp/foo")?;
assert_eq!(url.authority(), "");
let url = Url::parse("https://user:[email protected]/tmp/foo")?;
assert_eq!(url.authority(), "user:[email protected]");
pub fn cannot_be_a_base(&self) -> bool
pub fn username(&self) -> &str
let url = Url::parse("ftp://[email protected]")?;
assert_eq!(url.username(), "rms");
pub fn password(&self) -> Option<&str>
let url = Url::parse("ftp://:[email protected]")?;
assert_eq!(url.password(), Some("secret123"));
pub fn has_host(&self) -> bool
pub fn host_str(&self) -> Option<&str>
pub fn host(&self) -> Option<Host<&str>>
pub fn domain(&self) -> Option<&str>
pub fn port(&self) -> Option<u16>
pub fn port_or_known_default(&self) -> Option<u16>
pub fn socket_addrs( &self, default_port_number: impl Fn() -> Option<u16> )
-> Result<Vec<SocketAddr>, Error>
pub fn path(&self) -> &str
let url = Url::parse("https://example.com/countries/việt nam")?;
assert_eq!(url.path(), "/countries/vi%E1%BB%87t%20nam");
pub fn path_segments(&self) -> Option<Split<'_, char>>
pub fn query(&self) -> Option<&str>
fn run() -> Result<(), ParseError> {
let url = Url::parse("https://example.com/products?page=2")?;
let query = url.query();
assert_eq!(query, Some("page=2"));
let url = Url::parse("https://example.com/?country=español")?;
let query = url.query();
assert_eq!(query, Some("country=espa%C3%B1ol"));
pub fn query_pairs(&self) -> Parse<'_>
pub fn fragment(&self) -> Option<&str>
let url = Url::parse("https://example.com/data.csv#row=4")?;
assert_eq!(url.fragment(), Some("row=4"));
pub fn set_fragment(&mut self, fragment: Option<&str>)
pub fn set_query(&mut self, query: Option<&str>)
let mut url = Url::parse("https://example.com/products")?;
assert_eq!(url.as_str(), "https://example.com/products");
url.set_query(Some("page=2"));
assert_eq!(url.as_str(), "https://example.com/products?page=2");
assert_eq!(url.query(), Some("page=2"));
pub fn query_pairs_mut(&mut self) -> Serializer<'_, UrlQuery<'_>>
pub fn set_path(&mut self, path: &str)
let mut url = Url::parse("https://example.com/api")?;
url.set_path("data/report.csv");
assert_eq!(url.as_str(), "https://example.com/data/report.csv");
assert_eq!(url.path(), "/data/report.csv");
pub fn path_segments_mut(&mut self) -> Result<PathSegmentsMut<'_>, ()>
pub fn set_port(&mut self, port: Option<u16>) -> Result<(), ()>
pub fn set_host(&mut self, host: Option<&str>) -> Result<(), ParseError>
pub fn set_ip_host(&mut self, address: IpAddr) -> Result<(), ()>
pub fn set_password(&mut self, password: Option<&str>) -> Result<(), ()>
pub fn set_username(&mut self, username: &str) -> Result<(), ()>
pub fn set_scheme(&mut self, scheme: &str) -> Result<(), ()>
pub fn from_file_path<P>(path: P) -> Result<Url, ()> where P: AsRef<Path>,
pub fn from_directory_path<P>(path: P) -> Result<Url, ()> where P: AsRef<Path>,
pub fn to_file_path(&self) -> Result<PathBuf, ()>
Url::parse()/parse_with_params() 均会对 URL 和 params 字符串进行 URL 编码:
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>>{
use reqwest::Url;
let url = Url::parse_with_params(
"https://example.net?abc=def %中&dont=clobberme#% 中",
&[("lang", "rust"), ("&browser", "servo se#"), ("%asdfa asdf", "a中文c")],
)?;
println!("url: {}", url);
Ok(())
}
// zj@a:~/.emacs.d/rust-playground/at-2024-06-05-202535$ cargo run
// Compiling foo v0.1.0 (/Users/alizj/.emacs.d/rust-playground/at-2024-06-05-202535)
// Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.28s
// Running `target/debug/foo`
// url: https://example.net/?abc=def%20%%E4%B8%AD&dont=clobberme&lang=rust&%26browser=servo+se%23&%25asdfa+asdf=a%E4%B8%AD%E6%96%87c#%%20%E4%B8%AD
2 Request/RequestBuilder #
Request 是可以被 Client::execute() 发送的请求:
pub struct Request { /* private fields */ }
// 创建 Request
pub fn new(method: Method, url: Url) -> Self
pub fn method(&self) -> &Method
pub fn method_mut(&mut self) -> &mut Method
pub fn url(&self) -> &Url
pub fn url_mut(&mut self) -> &mut Url
pub fn headers(&self) -> &HeaderMap
pub fn headers_mut(&mut self) -> &mut HeaderMap
// 返回的是 reqwest::Body struct 类型
pub fn body(&self) -> Option<&Body>
pub fn body_mut(&mut self) -> &mut Option<Body>
pub fn timeout(&self) -> Option<&Duration>
pub fn timeout_mut(&mut self) -> &mut Option<Duration>
pub fn version(&self) -> Version
pub fn version_mut(&mut self) -> &mut Version
pub fn try_clone(&self) -> Option<Request>
RequestBuilder 用来构造 Request:
- build() 返回构造的 Request;
- send() 构造并发送 Request,返回 Response;
pub struct RequestBuilder { /* private fields */ }
impl RequestBuilder
pub fn from_parts(client: Client, request: Request) -> RequestBuilder
// 为请求添加一个 header
pub fn header<K, V>(self, key: K, value: V) -> RequestBuilder
where
HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<Error>,
HeaderValue: TryFrom<V>,
<HeaderValue as TryFrom<V>>::Error: Into<Error>
// 将传入的 headers merge 到 Request 中
pub fn headers(self, headers: HeaderMap) -> RequestBuilder
// 设置认证
pub fn basic_auth<U, P>( self, username: U, password: Option<P> ) -> RequestBuilder where U: Display, P: Display
pub fn bearer_auth<T>(self, token: T) -> RequestBuilder where T: Display
// 设置请求 body
pub fn body<T: Into<Body>>(self, body: T) -> RequestBuilder
// 设置 Request timeout,从开始建立连接到响应 Body 结束。只影响这一次请求,重载
// ClientBuilder::timeout() 定义的超时。
pub fn timeout(self, timeout: Duration) -> RequestBuilder
// 设置 HTTP 版本
pub fn version(self, version: Version) -> RequestBuilder
// 设置 URL query string,添加指定的 query 内容
pub fn query<T: Serialize + ?Sized>(self, query: &T) -> RequestBuilder
// 例如:.query(&[("foo", "a"), ("foo", "b")]) gives "foo=a&foo=b".
// 设置 Body 为传入的 URL 编码的表单数据,同时设置 Content-Type: application/x-www-form-urlencoded
// 自动对 form 数据进行 URL 编码。
pub fn form<T: Serialize + ?Sized>(self, form: &T) -> RequestBuilder
// 发送一个 multipart/form-data body,可以包含表单数据和文件数据
pub fn multipart(self, multipart: Form) -> RequestBuilder
pub fn json<T: Serialize + ?Sized>(self, json: &T) -> RequestBuilder
pub fn fetch_mode_no_cors(self) -> RequestBuilder
// 构造 Request
pub fn build(self) -> Result<Request>
pub fn build_split(self) -> (Client, Result<Request>)
// 构造并发送 Request,返回 Response
pub fn send(self) -> impl Future<Output = Result<Response, Error>>
pub fn try_clone(&self) -> Option<RequestBuilder>
Request 对象的 form() 和 multipart() 方法的区别:
- form() 方法:传入一个可以 Serialize 对象,对其各字段进行 URL 编码,设置
Content-Type: application/x-www-form-urlencoded
; - multipart() 方法:传入一个 multipart::Form 对象,设置
Content-Type: multipart/form-data
;
一般使用 Client 提供的方法来创建 RequestBuilder,如 reqwest::Client::new().get(url)
:
impl Client
// 创建一个缺省配置的 Client
pub fn new() -> Client
// 创建一个 RequestBuilder
pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder
pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder
pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder
pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder
pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder
pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder
pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder
示例:
let client = reqwest::Client::new(); // 创建缺省配置的、可复用的 Client(没有使用 ClientBuilder)
let resp = client.delete("http://httpbin.org/delete") // 创建 RequestBuilder
.basic_auth("admin", Some("good password")) // 设置 RequestBuilder
.send() // 发送请求获得响应
.await?;
// 发送 mulitipart 表单和文件数据
let form = reqwest::multipart::Form::new().text("key3", "value3").text("key4", "value4");
let response = client
.post("your url") // 返回 RequestBuilder
.multipart(form) // 设置 RequestBuilder
.send()
.await?;
// 发送 form 表单数据
let mut params = HashMap::new();
params.insert("lang", "rust# 中"); // 会进行 URL 编码
let client = reqwest::Client::new();
let req = client
.post("http://httpbin.org")
.form(¶ms).build().unwrap();
let bytes = req.body().unwrap().as_bytes().unwrap();
println!("req body: {:?}", String::from_utf8_lossy(bytes));
// req body: "lang=rust%23+%E4%B8%AD"
// 发送 JSON 数据
let mut map = HashMap::new();
map.insert("lang", "rust# %#"); // 不会进行 URL 编码
map.insert("body", "json");
let client = reqwest::Client::new();
let res = client
.post("http://httpbin.org/post")
.json(&map)
.build()
.unwrap();
let bytes = res.body().unwrap().as_bytes().unwrap();
println!("req json: {:?}", String::from_utf8_lossy(bytes));
// req json: "{\"lang\":\"rust# %#\",\"body\":\"json\"}"
3 multipart Form/Part #
使用 multipart/form 需要开启 multipart feature。 multipart/form-data
MIME 类型用于提交包含文件和二进制数据的表单,适用于文件上传等场景。结构如下:
- 边界 :开始部分
--boundary
- 头部 :包含内容描述 Header,如
Content-Disposition
和Content-Type
- 空行 :用于分隔头部和内容
- 内容 :表单字段或文件数据
- 边界 :结束部分
--boundary--
POST /upload HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="field1"
value1
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="field2"
value2
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="example.txt"
Content-Type: text/plain
This is the content of the file.
------WebKitFormBoundary7MA4YWxkTrZu0gW--
Form 是一个异步的 multipart/form-data
请求,支持同时上传表单数据和文件数据:
impl Form
pub fn new() -> Form
// 返回该 From 使用的 boundary 字符串
pub fn boundary(&self) -> &str
// 添加表单数据
pub fn text<T, U>(self, name: T, value: U) -> Form where T: Into<Cow<'static, str>>, U: Into<Cow<'static, str>>
// 添加文件数据,part 参数为 multipart::Part 对象,包含 bytes 和 filename
pub fn part<T>(self, name: T, part: Part) -> Form where T: Into<Cow<'static, str>>
pub fn percent_encode_path_segment(self) -> Form
pub fn percent_encode_attr_chars(self) -> Form
pub fn percent_encode_noop(self) -> Form
reqwest::multipart::Part 是 multipart 的一个 part 部分,可以是表单或文件数据:
- text() 表单数据;
- bytes() 任意二进制数据;
- mime_str() 设置本 part 的 MIME 类型;
- file_name() 设置本 part 的 filename;
- headers() 设置本 part 的自定义 header;
impl Part
// 文本数据
pub fn text<T>(value: T) -> Part where T: Into<Cow<'static, str>>
// 二进制数据
pub fn bytes<T>(value: T) -> Part where T: Into<Cow<'static, [u8]>>
pub fn stream<T: Into<Body>>(value: T) -> Part
pub fn stream_with_length<T: Into<Body>>(value: T, length: u64) -> Part
pub fn mime_str(self, mime: &str) -> Result<Part>
// 文件名
pub fn file_name<T>(self, filename: T) -> Part where T: Into<Cow<'static, str>>
// http header
pub fn headers(self, headers: HeaderMap) -> Part
示例:使用 multipart/form-data 上传表单和文件:
- 先创建一个 multipart::Form 对象,.text() 设置表单,.part() 设置文件;
- 调用 request::multipart() 方法,传入上一步创建的 multipart form 对象;
use reqwest::multipart;
use reqwest::Client;
use std::fs;
use tokio;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 创建 HTTP 客户端
let client = Client::new();
// 读取文件内容
let file_content = fs::read("path/to/your/file")?;
// 创建多部分表单
let form = multipart::Form::new()
.text("field1", "value1")
.text("field2", "value2")
.part("file", multipart::Part::bytes(file_content).file_name("your_file.txt"));
// 发送 POST 请求
let response = client
.post("http://example.com/upload")
.multipart(form)
.send()
.await?;
// 检查响应状态
if response.status().is_success() {
println!("Upload successful!");
} else {
println!("Upload failed: {:?}", response.status());
}
Ok(())
}
4 Body #
两类 Body:
- Body trait:在 http_body crate 中定义,在 hyper/axum/reqwest 中得到复用;
- Body struct:在 reqwest::Body 中定义, 实现了
http_body::Body trait
;
reqwest::Body 没有构建方法,但是实现了多种类型 From trait 的转换:
pub struct Body { /* private fields */ }
impl Body
pub fn as_bytes(&self) -> Option<&[u8]>
pub fn wrap_stream<S>(stream: S) -> Body
where
S: TryStream + Send + Sync + 'static,
S::Error: Into<Box<dyn Error + Send + Sync>>,
Bytes: From<S::Ok>
// 实现 http_body::Body trait,返回的 Data 类型是 bytes::Bytes,可以当作 &[u8] 来使用。
impl Body for Body
type Data = Bytes
type Error = Error
// 创建 Body
impl From<&'static [u8]> for Body
impl From<&'static str> for Body
impl From<Bytes> for Body
impl From<File> for Body
impl From<Response> for Body
impl From<String> for Body
impl From<Vec<u8>> for Body
Body 的使用场景:
- reqwest::Request::body() : 获取 Body;
- reqwest::RequestBuilder::body(): 设置 Body;
5 Client/ClientBuilder #
Client 对象用于构造 RequestBuilder 和发送 Request,它内部使用连接池和 Arc,是线程安全的:
impl Client
// 创建一个缺省配置的 Client
pub fn new() -> Client
// 自定义 Client
pub fn builder() -> ClientBuilder
// 创建 RequestBuilder,后续可以使用 ReqeustBuilder 的方法进一步配置
pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder
pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder
pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder
pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder
pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder
pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder
pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder
// 异步执行请求(返回一个 Future)
pub fn execute(&self, request: Request) -> impl Future<Output = Result<Response, Error>>
ClientBuilder 用于自定义 Client 配置:
impl ClientBuilder
pub fn new() -> ClientBuilder
pub fn build(self) -> Result<Client> // 返回一个 Client
pub fn user_agent<V>(self, value: V) -> ClientBuilder where V: TryInto<HeaderValue>, V::Error: Into<Error>
// 为每个 reqeust 设置的缺省 header
pub fn default_headers(self, headers: HeaderMap) -> ClientBuilder
// 缺省情况下,不开启 cookie sotre,需要调用以下两个方法之一来开启
// 开启并使用缺省的 cookie store
pub fn cookie_store(self, enable: bool) -> ClientBuilder
// 开启并使用指定的 cookiestore
pub fn cookie_provider<C: CookieStore + 'static>(self, cookie_store: Arc<C>) -> ClientBuilder
pub fn gzip(self, enable: bool) -> ClientBuilder
pub fn brotli(self, enable: bool) -> ClientBuilder
pub fn zstd(self, enable: bool) -> ClientBuilder
pub fn deflate(self, enable: bool) -> ClientBuilder
pub fn no_gzip(self) -> ClientBuilder
pub fn no_brotli(self) -> ClientBuilder
pub fn no_zstd(self) -> ClientBuilder
pub fn no_deflate(self) -> ClientBuilder
pub fn redirect(self, policy: Policy) -> ClientBuilder
// 缺省为 true
pub fn referer(self, enable: bool) -> ClientBuilder
pub fn proxy(self, proxy: Proxy) -> ClientBuilder
pub fn no_proxy(self) -> ClientBuilder
// 从建立连接到读取 body 结束的超时时间(默认不超时)
pub fn timeout(self, timeout: Duration) -> ClientBuilder
// 每次 read 超时时间,read 成功后重置(默认不超时)
pub fn read_timeout(self, timeout: Duration) -> ClientBuilder
// 建立连接超时时间(默认不超时)
pub fn connect_timeout(self, timeout: Duration) -> ClientBuilder
// 启用后,发送 TRACE 级别的 log
pub fn connection_verbose(self, verbose: bool) -> ClientBuilder
// 连接池空闲时间(默认 90s)
pub fn pool_idle_timeout<D>(self, val: D) -> ClientBuilder where D: Into<Option<Duration>>
// 默认无限制
pub fn pool_max_idle_per_host(self, max: usize) -> ClientBuilder
// Send headers as title case instead of lowercase.
pub fn http1_title_case_headers(self) -> ClientBuilder
pub fn http1_allow_obsolete_multiline_headers_in_responses(self, value: bool ) -> ClientBuilder
pub fn http1_ignore_invalid_headers_in_responses( self, value: bool) -> ClientBuilder
pub fn http1_allow_spaces_after_header_name_in_responses( self, value: bool ) -> ClientBuilder
pub fn http1_only(self) -> ClientBuilder
pub fn http09_responses(self) -> ClientBuilder
pub fn http2_prior_knowledge(self) -> ClientBuilder
pub fn http2_initial_stream_window_size( self, sz: impl Into<Option<u32>> ) -> ClientBuilder
pub fn http2_initial_connection_window_size( self, sz: impl Into<Option<u32>> ) -> ClientBuilder
pub fn http2_adaptive_window(self, enabled: bool) -> ClientBuilder
pub fn http2_max_frame_size(self, sz: impl Into<Option<u32>>) -> ClientBuilder
pub fn http2_keep_alive_interval( self, interval: impl Into<Option<Duration>>) -> ClientBuilder
pub fn http2_keep_alive_timeout(self, timeout: Duration) -> ClientBuilder
pub fn http2_keep_alive_while_idle(self, enabled: bool) -> ClientBuilder
// 缺省为 true
pub fn tcp_nodelay(self, enabled: bool) -> ClientBuilder
pub fn local_address<T>(self, addr: T) -> ClientBuilder where T: Into<Option<IpAddr>>
pub fn interface(self, interface: &str) -> ClientBuilder
pub fn tcp_keepalive<D>(self, val: D) -> ClientBuilder where D: Into<Option<Duration>>
// 添加自定义 root 证书
pub fn add_root_certificate(self, cert: Certificate) -> ClientBuilder
// 缺省为 true,使用系统 root 证书,设置为 false 时只使用添加的证书
pub fn tls_built_in_root_certs( self, tls_built_in_root_certs: bool ) -> ClientBuilder
pub fn tls_built_in_webpki_certs(self, enabled: bool) -> ClientBuilder
pub fn tls_built_in_native_certs(self, enabled: bool) -> ClientBuilder
// 指定 client 证书
pub fn identity(self, identity: Identity) -> ClientBuilder
// 接受无效的证书
pub fn danger_accept_invalid_hostnames( self, accept_invalid_hostname: bool ) -> ClientBuilder
pub fn danger_accept_invalid_certs( self, accept_invalid_certs: bool) -> ClientBuilder
// 默认为 true
pub fn tls_sni(self, tls_sni: bool) -> ClientBuilder
pub fn min_tls_version(self, version: Version) -> ClientBuilder
pub fn max_tls_version(self, version: Version) -> ClientBuilder
// tls backend 选择
pub fn use_native_tls(self) -> ClientBuilder
pub fn use_rustls_tls(self) -> ClientBuilder
pub fn use_preconfigured_tls(self, tls: impl Any) -> ClientBuilder
// Add TLS information as TlsInfo extension to responses
pub fn tls_info(self, tls_info: bool) -> ClientBuilder
// 默认为 false
pub fn https_only(self, enabled: bool) -> ClientBuilder
// Enables the hickory-dns async resolver instead of a default threadpool using getaddrinfo.
// If the hickory-dns feature is turned on, the default option is enabled.
pub fn hickory_dns(self, enable: bool) -> ClientBuilder
pub fn no_trust_dns(self) -> ClientBuilder
pub fn no_hickory_dns(self) -> ClientBuilder
// 将 domain 的地址重写为指定值
pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder
// 将 domain 的域名解析结果重写为指定 slice
pub fn resolve_to_addrs( self, domain: &str, addrs: &[SocketAddr]) -> ClientBuilder
// 使用指定的 DNS Resolve 实现
pub fn dns_resolver<R: Resolve + 'static>( self, resolver: Arc<R>) -> ClientBuilder
示例:
// Name your user agent after your app?
static APP_USER_AGENT: &str = concat!(
env!("CARGO_PKG_NAME"),
"/",
env!("CARGO_PKG_VERSION"),
);
let client = reqwest::Client::builder()
.user_agent(APP_USER_AGENT)
.build()?;
let res = client
.get("https://www.rust-lang.org") // 创建一个 RequestBuilder
.send() // 异步发送 Request
.await?;
use reqwest::header;
let mut headers = header::HeaderMap::new();
headers.insert("X-MY-HEADER", header::HeaderValue::from_static("value"));
// Consider marking security-sensitive headers with `set_sensitive`.
let mut auth_value = header::HeaderValue::from_static("secret");
auth_value.set_sensitive(true);
headers.insert(header::AUTHORIZATION, auth_value);
// get a client builder
let client = reqwest::Client::builder()
.default_headers(headers)
.build()?;
let res = client.get("https://www.rust-lang.org").send().await?;
6 Response #
pub struct Response { /* private fields */ }
impl Response
pub fn status(&self) -> StatusCode
pub fn version(&self) -> Version
pub fn headers(&self) -> &HeaderMap
pub fn headers_mut(&mut self) -> &mut HeaderMap
pub fn content_length(&self) -> Option<u64>
pub fn cookies<'a>(&'a self) -> impl Iterator<Item = Cookie<'a>> + 'a
pub fn url(&self) -> &Url
pub fn remote_addr(&self) -> Option<SocketAddr>
pub fn extensions(&self) -> &Extensions
pub fn extensions_mut(&mut self) -> &mut Extensions
// 返回 utf-8 编码的 body 文本
pub async fn text(self) -> Result<String>
pub async fn text_with_charset(self, default_encoding: &str) -> Result<String>
let content = reqwest::get("http://httpbin.org/range/26").await?.text().await?;
println!("text: {content:?}");
pub async fn json<T: DeserializeOwned>(self) -> Result<T>
#[derive(Deserialize)]
struct Ip { origin: String, }
let ip = reqwest::get("http://httpbin.org/ip") .await? .json::<Ip>() .await?;
println!("ip: {}", ip.origin);
pub async fn bytes(self) -> Result<Bytes> // bytes::Bytes 类型
let bytes = reqwest::get("http://httpbin.org/ip") .await? .bytes() .await?;
println!("bytes: {bytes:?}");
pub async fn chunk(&mut self) -> Result<Option<Bytes>> // 流式响应
let mut res = reqwest::get("https://hyper.rs").await?;
while let Some(chunk) = res.chunk().await? {
println!("Chunk: {chunk:?}");
}
pub fn bytes_stream(self) -> impl Stream<Item = Result<Bytes>>
use futures_util::StreamExt;
let mut stream = reqwest::get("http://httpbin.org/ip") .await? .bytes_stream();
while let Some(item) = stream.next().await {
println!("Chunk: {:?}", item?);
}
pub fn error_for_status(self) -> Result<Self>
pub fn error_for_status_ref(&self) -> Result<&Self>
pub async fn upgrade(self) -> Result<Upgraded>
7 Error #
Struct reqwest::Error 表示在发送 Reqeust 时遇到的错误:
pub struct Error { /* private fields */ }
// reqwest::RequestBuilder 的 send() 返回 Error
pub fn send(self) -> impl Future<Output = Result<Response, Error>>
// reqwest::Client 的 execute() 返回 Error
pub fn execute(
&self,
request: Request,
) -> impl Future<Output = Result<Response, Error>>
request::Error 类型的方法:
pub fn url(&self) -> Option<&Url>
pub fn url_mut(&mut self) -> Option<&mut Url>
// Error 信息默认打印请求的完整 URL,可能包含敏感信息,可以调用 without_url() 来清理不显示。
pub fn with_url(self, url: Url) -> Self
pub fn without_url(self) -> Self
// 产生该 Error 相关的各种原因
pub fn is_builder(&self) -> bool
pub fn is_redirect(&self) -> bool
pub fn is_status(&self) -> bool
pub fn is_timeout(&self) -> bool
pub fn is_request(&self) -> bool
pub fn is_connect(&self) -> bool
pub fn is_body(&self) -> bool
pub fn is_decode(&self) -> bool
// Returns the status code, if the error was generated from a response.
pub fn status(&self) -> Option<StatusCode>
示例:
// displays last stop of a redirect loop
let response = reqwest::get("http://site.with.redirect.loop").await;
if let Err(e) = response {
if e.is_redirect() {
if let Some(final_stop) = e.url() {
println!("redirect loop at {final_stop}");
}
}
}
use http::StatusCode;
assert_eq!(StatusCode::from_u16(200).unwrap(), StatusCode::OK);
assert_eq!(StatusCode::NOT_FOUND.as_u16(), 404);
assert!(StatusCode::OK.is_success());