0

Ok, so I'm very new to Rust and I'm trying to clumsily piece together a little CLI tool that makes http requests and handles the responses, by using tokio, clap, reqwest and serde.

The tool accepts a customer number as input and then it tries to fetch information about the customer. The customer may or may not have a FooBar in each country.

My code currently only works if I get a nice 200 response containing a FooBar. If I don't, the deserialization fails (naturally). (Edit: Actually, this initial assumption about the problem seems to be false, see comments below)

My aim is to only attempt the deserialization if I actually get a valid response.

How would I do that? I feel the need to see the code of a valid approach to understand this better.

Below is the entirety of my program.


use clap::Parser;
use reqwest::Response;
use serde::{Deserialize, Serialize};

#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let args: Cli = Cli::parse();

    let client = reqwest::Client::new();

    let countries = vec!["FR", "GB", "DE", "US"];

    for country in countries.iter() {
        let foo_bar : FooBar = client.get(
            format!("http://example-service.com/countries/{}/customers/{}/foo_bar", country, args.customer_number))
            .send()
            .await?
            .json()
            .await?;

        println!("{}", foo_bar.a_value);
    }

    Ok(())
}

#[derive(Debug, Serialize, Deserialize)]
struct FooBar {
    a_value: String,
}

#[derive(Parser, Debug)]
struct Cli {
    customer_number: i32,
}
habjo
  • 19
  • 8
  • 1
    The `?` operator returns any errors that occur in `send` and `json` methods from the `main` function. So if the sending and getting a response fails (like when you get 404 error) you are early returning and the `json` function is never called. – Aleksander Krauze Oct 08 '22 at 19:19
  • Hm...ok. So my assumption about the problem I have is wrong then? Interesting. Thank you. I assumed it was because it couldn't deserialize the 404 responses in some of the cases, but now I realize that's actually not the problem at all.. :) – habjo Oct 08 '22 at 19:23
  • 1
    `send().await` does not return an error on HTTP error codes such as `404`, for that behavior you would have to call `error_for_status()` or `error_for_status_ref()` on the response returned by `send().await`. – sebpuetz Oct 08 '22 at 19:25

1 Answers1

1

There are a few ways to approach this issue, first of all you can split the json() deserialization from send().await, i.e.:

    for country in countries.iter() {
        let resp: reqwest::Response = client.get(
            format!("http://example-service.com/countries/{}/customers/{}/foo_bar", country, args.customer_number))
            .send()
            .await?;
        if resp.status() != reqwest::StatusCode::OK {
             eprintln!("didn't get OK status: {}", resp.status());
        } else {
             let foo_bar = resp.json().await?;
             println!("{}", foo_bar.a_value);
        }
    }

If you want to keep the response body around, you can extract it through let bytes = resp.bytes().await?; and pass bytes to serde_json::from_slice(&*bytes) for the deserialization attempt.

This can be useful if you have a set of expected error response bodies.

sebpuetz
  • 2,430
  • 1
  • 7
  • 15