jsonrustreqwest

Simpler way to generate json body for reqwest post


I have a post call that adds one line in register, it works but a pain in the back to write and look at. This is how it looks:

static CLIENT: Lazy<Client> = 
    Lazy::new(|| Client::new());
static GENERATED_TOKEN: Lazy<GeneratedToken> =
    Lazy::new(|| get_token().expect("failed to get token"));

fn get_token() -> Result<GeneratedToken, Box<dyn std::error::Error>> {
    ...
    Ok(generated_token)
}


fn register(email: &str, pw: &str) -> bool {
    let body = String::from("") + "{
        \"table_name\": \"admin\",
        \"row\": {
            \"email\": \"" + email + "\", \"password\": \"" + pw + "\"
        }
    }";

    let response = CLIENT
        .post(format!("{SEATABLE_BASE_URL}dtable-server/api/v1/dtables/{}/rows/", GENERATED_TOKEN.dtable_uuid))
        .header(AUTHORIZATION, format!("Bearer {}", GENERATED_TOKEN.access_token))
        .header(ACCEPT, "application/json")
        .header(CONTENT_TYPE, "application/json")
        .body(body)
        .send().is_ok();

    println!("Registration successful with email \"{email}\": {response}");
    response
}

fn main() {
    Lazy::force(&CLIENT);
    println!("Has CLIENT");
    Lazy::force(&GENERATED_TOKEN);
    println!("Has TOKEN?: {}", (GENERATED_TOKEN.access_token.len() > 0).to_string());

    register("email", "pw");
}

So how can I make the request body in a better way? I know there is a .json() method for reqwest, that sends a Map, but it didn't work for me.

These are the Cargo.toml dependencies:

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
reqwest = { version = "0.11", features = ["json", "blocking"] }
lazy_static = "1.4.0"
once_cell = "1.18.0"

Solution

  • You can use the json!() macro to write inline json using variables from the current scope:

    let json = &serde_json::json!({
        "table_name": "admin",
        "row": {
            "email": email,
            "password": pw
        }
    });
    
    let request = CLIENT.post("your_url")
        .json(json);
    

    But if you plan on working with the data more than just passing it to a RequestBuilder you should probably just define custom datatypes:

    fn register(u: &str, p: &str) {
        let request = Client::new().post("the url").json(&RegistrationData::new(u, p));
    }
    
    impl<'a> RegistrationData<'a> {
        fn new(user: &'a str, password: &'a str) -> Self {
            Data {
                table_name: "admin",
                row: Row { user, password },
            }
        }
    }
    
    #[derive(serde::Serialize)]
    struct RegistrationData<'a> {
        table_name: &'a str,
        row: Row<'a>,
    }
    #[derive(serde::Serialize)]
    struct Row<'a> {
        user: &'a str,
        password: &'a str,
    }