Introduction
There are 3 things that need to happen:
- Building a client that can be reused across multiple requests
- Performing the execution of the network request
- Parsing the response
use std::collections::HashMap;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Build the client using the builder pattern
let client = reqwest::Client::builder()
.build()?;
// Perform the actual execution of the network request
let res = client
.get("https://httpbin.org/ip")
.send()
.await?;
// Parse the response body as Json in this case
let ip = res
.json::<HashMap<String, String>>()
.await?;
println!("{:?}", ip);
Ok(())
}
Instead of parsing the json body as a dynamic hash-map, you could use a well-defined struct
use serde::Deserialize;
#[derive(Deserialize, Debug)]
struct Ip {
origin: String
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Build the client using the builder pattern
let client = reqwest::Client::builder()
.build()?;
// Perform the actual execution of the network request
let res = client
.get("https://httpbin.org/ip")
.send()
.await?;
// Parse the response body as Json in this case
let ip = res
.json::<Ip>()
.await?;
println!("{:?}", ip);
Ok(())
}
Query Strings
let client = reqwest::Client::builder()
.build()?;
let res = client
.get("http://httpbin.org")
.query(&[("foo", "bar")])
.send()?;
POST JSON Data
The json(..)
method on the RequestBuilder
takes any value that can be serialized into JSON such as a HashMap or a Struct.
let mut map = HashMap::new();
map.insert("foo", "bar");
map.insert("buzz", "blah");
...
.json(&map)
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Serialize, Deserialize)]
struct Person {
first_name: String,
last_name: String,
}
#[derive(Debug, Serialize, Deserialize)]
struct PersonResponse {
data: String,
method: String,
headers: HashMap<String, String>
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let p = Person {
first_name: "Foo".into(),
last_name: "Bar".into(),
};
let res = reqwest::Client::new()
.post("https://httpbin.org/anything")
.json(&p)
.send()
.await?;
let js = res
.json::<PersonResponse>()
.await?;
let person: Person = serde_json::from_str(&js.data)?;
println!("{:#?}", person);
println!("Headers: {:#?}", js.headers);
Ok(())
}
POST Form data
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct Person {
first_name: String,
last_name: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let p = Person {
first_name: "Foo".into(),
last_name: "Bar".into(),
};
let res = reqwest::Client::new()
.post("https://httpbin.org/anything")
.form(&p)
.send()
.await?;
let t = res
.text()
.await?;
println!("{}", t);
// $ cargo run
// Finished dev [unoptimized + debuginfo] target(s) in 0.09s
// Running `target/debug/reqwest-tut`
// {
// "args": {},
// "data": "",
// "files": {},
// "form": {
// "first_name": "Foo",
// "last_name": "Bar"
// },
// "headers": {
// "Accept": "*/*",
// "Content-Length": "28",
// "Content-Type": "application/x-www-form-urlencoded",
// "Host": "httpbin.org",
// "X-Amzn-Trace-Id": "Root=1-60453174-384d97870199933007fbb388"
// },
// "json": null,
// "method": "POST",
// "origin": "125.125.104.53",
// "url": "https://httpbin.org/anything"
// }
//
//
Ok(())
}
Request Headers
You can set the headers by calling the header(.., ..)
method on the request
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = reqwest::Client::builder()
.build()?;
let res = client
.post("https://httpbin.org/anything")
.body("arbitrary text")
.header("X-Person-First", "Foo!")
.header("X-Person-Last", "Bar!!")
.send()
.await?;
let t = res
.text()
.await?;
println!("{}", t);
// {
// "args": {},
// "data": "arbitrary text",
// "files": {},
// "form": {},
// "headers": {
// "Accept": "*/*",
// "Content-Length": "14",
// "Host": "httpbin.org",
// "X-Amzn-Trace-Id": "Root=1-604538df-218a5bb97264e7130c298b23",
// "X-Person-First": "Foo!",
// "X-Person-Last": "Bar!!"
// },
// "json": null,
// "method": "POST",
// "origin": "49.206.4.160",
// "url": "https://httpbin.org/anything"
// }
Ok(())
}
You can also set default headers on the client that can be overriden while making individual requests
use reqwest::header;
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let mut headers = header::HeaderMap::new();
headers.insert("X-HEADER-1", header::HeaderValue::from_static("val1"));
headers.insert("X-HEADER-2", header::HeaderValue::from_static("val2"));
let client = reqwest::Client::builder()
.default_headers(headers)
.build()?;
let res = client
.get("https://httpbin.org/anything")
.body("whatever")
.header("X-HEADER-1", "overriden val1")
.send()
.await?;
println!("{}", res.text().await?);
// {
// "args": {},
// "data": "whatever",
// "files": {},
// "form": {},
// "headers": {
// "Accept": "*/*",
// "Content-Length": "8",
// "Host": "httpbin.org",
// "X-Amzn-Trace-Id": "Root=1-60453dad-429e59a434bd460b0a48e7d5",
// "X-Header-1": "overriden val1",
// "X-Header-2": "val2"
// },
// "json": null,
// "method": "GET",
// "origin": "49.206.4.160",
// "url": "https://httpbin.org/anything"
// }
Ok(())
}
Status Code
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = reqwest::Client::builder().build()?;
let res = client
.get("https://httpbin.org/status/400")
.send()
.await?;
match res.status() {
reqwest::StatusCode::BAD_REQUEST => println!(
"content-length:{:?} server:{:?}",
res.headers().get(reqwest::header::CONTENT_LENGTH),
res.headers().get(reqwest::header::SERVER),
),
status => println!("status: {}", status),
}
// content-length:Some("0") server:Some("gunicorn/19.9.0")
Ok(())
}
Timeouts
Client Timeout
let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_millis(500))
.build()?;
Connect Timeout
let client = reqwest::Client::builder()
.connect_timeout(std::time::Duration::from_millis(100))
.build()?;
Request Timeout
let res = client
.get(&url)
.timeout(std::time::Duration::from_millis(500))
.send()
.await;
Download data
use std::fs::File;
use std::io;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = reqwest::Client::builder().build()?;
let res = client
.get("https://httpbin.org/image/png")
.send()
.await?
.bytes()
.await?;
let mut data = res.as_ref();
let mut f = File::create("i.png")?;
io::copy(&mut data, &mut f)?;
Ok(())
}
// $ file i.png
// i.png: PNG image data, 100 x 100, 8-bit/color RGB, non-interlaced
Dependency configuration for the samples
[dependencies]
reqwest = { version = "0.11", features = ["json", "blocking", "cookies"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1.0"
futures = "0.3"
Top comments (5)
Thanks!!
This is very helpful!
Nice It's very simple.
U - useful
Very useful
Thanks very useful !
I am completely new to Rust. If I want to use a request in another function how can I use the return ?