Basic way to send HTTP Request with different param/header/token in Rust (No Async/Await)

I am looking into some good HTTP library with Rust, to do get/post command. However, as I am new to Rust, I am not sure which library is a good place to start.
For example, there is a library in Go that can perform GET as follow:

resp, err := client.R().
      SetQueryParams(map[string]string{
          "page_no": "1",
          "limit": "20",
          "sort":"name"
      }).
      SetHeader("Accept", "application/json").
      SetHeader("Connection", "keep-alive").
      SetAuthToken("1234ABCD").
      Get("/search_result")

I do not need it to be asynchronous, but many examples with rusts are shown with async function, which further confused me. In the examples that I have come across, most requests involve async/await... I mean, why, was that really necessary, or is it just because the library was designed that way?
So anyway, I wonder which library in Rust would allow me to set various parameters, header or token as versatile (or in a similar form) as the previous syntax, which I presume it's very basic. Thank you.

Whats the reason why you don’t want to use async functions though? In case you’re just unsure how to call them in a synchronous context: there’s always the possibility to use block_on.

The reason why most APIs for networking are using async/await is: It is often not the case that you want to block your whole program, or a whole OS-thread to wait for an answer from the network. Another reason is that asynchronous API can still be used when you want to just block and wait for the result (as explained above, with e.g. "block_on"), while you cannot really use a synchronous API asynchronously (without the overhead of spawning lots of threads).

1 Like

It will over-compilate what I want to achieve with my program. I'm making a small GUI tool, basically a simple HTTP client, so I do not feel the need to handle asynchronous programming, which tbh I am not familiar with. But I will took into async/await if there is no other reliable alternative.

I’ve never used reqwest myself yet (so I have no idea if this kind of code actually works) but something like this compiles:

use futures::executor::block_on;
// or: use futures_executor::block_on;
// which brings in fewer dependencies

use reqwest::Client;
fn single_blocking_request() {
    let client = Client::new();
    match block_on(
        client
            .get("https://example.com/search_result")
            .query(&[("page_no", "1"), ("limit", "20"), ("sort", "name")])
            .header("Accept", "application/json")
            .header("Connection", "keep-alive")
            .bearer_auth("1234ABCD")
            .send(),
    ) {
        Ok(_response) => {
            // ...
        }
        Err(_error) => {
            // ...
        }
    }
}
1 Like

To use reqwest without async/await, enable its "blocking" feature in Cargo.toml:

[dependencies]
reqwest = { version = "0.10.9", features = ["blocking"] }

Then use the request::blocking module in your program:

use reqwest::blocking::Client;

fn main()  {
    let result = Client::new()
            .get("https://example.com/search_result")
            .query(&[("page_no", "1"), ("limit", "20"), ("sort", "name")])
            .header("Accept", "application/json")
            .header("Connection", "keep-alive")
            .bearer_auth("1234ABCD")
            .send();
            
    match result {
        Ok(response) => {}
        Err(err) => {}
    }
}
5 Likes

Nice, this does seem to work, but only if the response if in text form of sort. What if it's an image/binary data.
For example, this code

use reqwest::blocking::Client;

fn main()  {
    let result = Client::new()
            .get("https://pngimg.com/uploads/dog/dog_PNG50348.png")
            .send();
            
    match result {
        Ok(response) => {}
        Err(err) => {}
    }
}

Would basically get an image.
I presume it will get binary data. If so then how to save the image as a file, like "dog.png" or something. Thank you.

You can use Response::bytes to read a binary response into memory, or Response::copy_to to write it directly to a File.

use reqwest::blocking::get;

get("https://pngimg.com/uploads/dog/dog_PNG50348.png")?
    .copy_to(File::create("dog.png")?)?;

I tried with the previous way but it did not work.
So all in all, with this piece of code:

fn func4_get_req(){
    let result = Client::new()
        .get("https://pngimg.com/uploads/dog/dog_PNG50348.png")
        .send();

    match result {
        Ok(mut response) => {
            response.copy_to(&mut File::create("dog.png"));
        }
        Err(err) => {}
    }

}

I got

error[E0277]: the trait bound `std::result::Result<std::fs::File, std::io::Error>: std::io::Write` is not satisfied
  --> src\main.rs:84:30
   |
84 |             response.copy_to(&mut File::create("dog.png"));
   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::io::Write` is not implemented for `std::result::Result<std::fs::File, std::io::Error>`

What should I do to mitigate it?

File::create does not return a File but a Result<File> (file creation can fail) which, as the compiler is telling you, does not implement the std::io::Write trait and you need something that does implement that trait.

Since File implements std::io::Write and you have a Result<File> you just need to handle the Result bit (determine whether the file was created successfully, in which case you'll have a File to write to) either via matching (or if let) but for learning and building a proof-of-concept you will, I think, be fine to use response.copy_to(&mut File::create("dog.png").unwrap()) (read more about unwrap).

1 Like

This works wonderfully. Thank you.