In Rust's backend, with http GET request, how to get array in query parameter

154 views Asked by At

Array data has 4 forms in query parameter:

  1. ?id=1&id=2
  2. ?id=1,2
  3. ?id[]=1&id[]=2
  4. ?id[0]=1&id[1]=2 \

serde and serde_qs can only support 3&4 ("duplicated key" when use 1, and "need sequence but found string" when use 2). I'm developing API which can only accept 2. So I have to create an intermediary struct to get every array value by String, and then split it into Vec<String> and move to new struct. Obviously, it's ugly, inefficient and difficult to maintain.

serde_qs have some issues about it, but it seems that its owner haven't resolve it yet. Are there any beautiful way to deserialize query params into a certain struct?

In addition, I'm using axum to develop a RESTful API. But without good deserialize tool, it's very hard to develop complex GET requests. If there are any functions work better than serde_urlencoded (which is executed at axum::extract::Query), we could create CostomQuery to easily deserialize more data structure.

2

There are 2 answers

1
Masklinn On

(2) you can handle reasonably easily via serde's (de)serialize_with and a custom (de)serialisation function, or serde_with's fromstr support (and a newtype implementing FromStr):

#[derive(Debug, Deserialize)]
struct Foo {
    #[serde(deserialize_with = "from_seq")]
    id: Vec<u8>,
}

fn from_seq<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
    D: Deserializer<'de>,
{
    let s = <&str>::deserialize(deserializer)?;

    s.split(',')
        .map(|i| u8::from_str(i))
        .collect::<Result<Vec<_>, _>>()
        .map_err(serde::de::Error::custom)
}

=>

id=1,2 => Ok(Foo { id: [1, 2] })

(1) I think you need a full blown hand-implemented deserializer on your type to correctly handle duplicate keys, I've not seen a third-party crate which handled that use case (serde_with as a hook to trigger an error on duplicate map entries, but nothing to control the behaviour of duplicate struct fields), although it's possible I missed it.

0
Ayana-chan On

I published a crate to deserialize form style parameter depend on FromStr trait. It's more generic. Here. It can easily deserialze Vec<T: FromStr> and Option<Vec<T: FromStr>>, and any T impl FromStr (which also supported by serde_with).