actix-web: Parsing optional field seems to not work for numbers when not filled

Hello, I currently try to parse a form but I have a Parse error for a specific case:

struct TestForm {
    text: Option<String>,
    number: Option<u32>
} 
  1. Whether I filled the text input field or not, it ends up being `Some(“whatever”) or ‘None’.
  2. When I fill the number input field (a html text input), It parses to Some(123).
  3. But when I don’t fill the number field, it fails and return a Parse error (a UrlencodedError::Parse error apparently)

Same behaviour for f32 so far I’ve tested.

  • Rust Version: 1.48.0
  • Actix Web Version: 3.3.2

Thanks a lot for the help.

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Comments: 15 (7 by maintainers)

Most upvoted comments

Slightly less involved workaround:

pub fn deserialize_option_ignore_error<'de, T, D>(d: D) -> Result<Option<T>, D::Error>
where
    T: de::Deserialize<'de>,
    D: de::Deserializer<'de>,
{
    Ok(T::deserialize(d).ok())
}

#[derive(Debug, Deserialize)]
struct FormData {
    text: Option<String>,

    #[serde(default, deserialize_with = "deserialize_option_ignore_error")]
    number: Option<u32>,
}

I understand and agree that this behaviour is correct from a query parser point of view: text&number=32 should not produce text: None. Maybe the lib implementation is correct if you don’t consider what sends the request. But well, this is the title: x-www-form-urlencoded meets Serde

Here we are talking about web::Form which is a Form deserializer utility and imho it should behave like a form behaves.

So, regarding browsers behaviour, a required input field is not valid until the text is not empty. From MDN:

The Boolean required attribute which, if present, indicates that the user must specify a value for the input before the owning form can be submitted

Conversely, an empty string for an optional input (default) has no value, it is None then. A web form sends all data, filled or not. I think we can’t even discuss if an empty string is valuable or not, the web standard decided for us.

Unfortunately, in the current implementation state, it makes the deserialization completely useless for optional inputs as we have to treat everything as String and do the job ourselves anyway.

I think your solution is pretty neat but do you think this kind of problem should be handled by the user/developer using actix-web instead of having actix-web handle it for them?

Absolutely not. Framework should handle this.

If actix-web were to manage it for them, maybe a rewrite of serde_urlencoded with some specific changes for actix-web could be useful?

As this is the only open issue regarding problems with serde_urlencoded, I’m considering it a tracking issue for a remedy.

@robjtede That workaround is incredibly helpful, thank you!

In my situation, that helped me to have more graceful failures when query params aren’t right (either empty like someNumber= which is easy to obtain through a form with a GET action, or someone URL hacking and setting someNumber=asdf in which case I’d prefer for it to just be ignored and treated as None rather than seeing an error page).