DEV Community

pintuch
pintuch

Posted on

33 5 1 1 1

Rust - Reqwest examples

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(())
}
Enter fullscreen mode Exit fullscreen mode

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(())
}
Enter fullscreen mode Exit fullscreen mode

Query Strings

let client = reqwest::Client::builder()
    .build()?;

let res = client
    .get("http://httpbin.org")
    .query(&[("foo", "bar")])
    .send()?;
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode
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(())
}

Enter fullscreen mode Exit fullscreen mode

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(())
}

Enter fullscreen mode Exit fullscreen mode

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(())
}

Enter fullscreen mode Exit fullscreen mode

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(())
}
Enter fullscreen mode Exit fullscreen mode

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(())
}
Enter fullscreen mode Exit fullscreen mode

Timeouts

Client Timeout

let client = reqwest::Client::builder()
    .timeout(std::time::Duration::from_millis(500))
    .build()?;

Enter fullscreen mode Exit fullscreen mode

Connect Timeout

let client = reqwest::Client::builder()
    .connect_timeout(std::time::Duration::from_millis(100))
    .build()?;
Enter fullscreen mode Exit fullscreen mode

Request Timeout

let res = client
    .get(&url)
    .timeout(std::time::Duration::from_millis(500))
    .send()
    .await;
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode
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"
Enter fullscreen mode Exit fullscreen mode

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

Top comments (5)

Collapse
 
summerbud profile image
Summerbud

Thanks!!

This is very helpful!

Collapse
 
friendbear profile image
T Kumagai

Nice It's very simple.

Collapse
 
raduc4 profile image
Raduc4

U - useful

Collapse
 
email2vimalraj profile image
Vimalraj Selvam

Very useful

Collapse
 
donovan1905 profile image
Donovan Hoang

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 ?

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay