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,
}
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.