Version
warp: 0.2.5
Platform
Linux 5.4.72-1-lts, ArchLinux, x86_64
Rust 1.49.0-nightly
Description
Lists encoded in application/x-www-form-urlencoded POST requests are not correctly deserialized.
For example, this HTML form:
<form method="post">
<input type="checkbox" name="foo" value="1"/>
<input type="checkbox" name="foo" value="2"/>
<input type="checkbox" name="foo" value="3"/>
<input type="submit"/>
</form>
And this Rust code:
warp::serve(
warp::post().and(
warp::body::form::<FooQuery>()
.map(|q: FooQuery| format!("Request: {:?}", q))
)
.or(warp::get().map(handle_index)) // handle_index just serves HTML page
).run(address).await;
#[derive(Clone, Debug, Deserialize)]
struct FooQuery {
foo: Vec<u32>,
}
The request's body can be foo=2&foo=3 for example. It should be interpreted as FooQuery { foo: vec![2, 3] }, but there is an error: Request body deserialize error: invalid type: string "2", expected a sequence.
PHP needs field names to end with [] to interpret them as a list. I tried that but there is also an error.
Hi, warp uses serde-urlencoded for that feature, see https://github.com/nox/serde_urlencoded/issues/52#issuecomment-483138575 and https://github.com/nox/serde_urlencoded/issues/75#issuecomment-648257888
@jxs Does that mean that it is not possible to parse vectors from a GET URL unless you choose to parse the raw string on your own?
I ask because my issue is related to this https://github.com/seanmonstar/warp/issues/732
@Gelox yeah, I was just going to answer there, query filter also uses serde_urlencoded
@jxs Is there interest in updating this so vectors are supported via get requests? I understand serde is not interested in changing this but it seems like a modern HTTP library would want to handle vector support in some way.
Have similar issues been closed with the decision to not support this?
@Gelox yeah, similar issues have been raised: https://github.com/seanmonstar/warp/issues/677, https://github.com/seanmonstar/warp/issues/22 and on the discord channel a couple times. So maybe we could consider to switch to serde_qs since serde_urlencoded is not interested in offering those features, what do you think @seanmonstar ?
As far as I can tell serde_qs does not have the same list syntax for query strings that is usually the case for web frameworks, at least from the two sources I posted in the other issue stackoverflow and wiki.
serde_qs wants an array to be indexed like so
/get_path?list[0]=a&list[1]=b
while the (not standardized) syntax in the links is either
/get_path?list=a&list=b
or
/get_path?list[]=a&list[]=b
of course the syntax in serde_qs does make it clearer what the ordering will be in a vector but it might be a concern that it requires users to use a different syntax than they are used to for specifying lists.
Of course serde_qs is better than nothing, but perhaps some additions can be made to the implementations? I will ask them what their thoughts on this are.
@Gelox :
@jxs Does that mean that it is not possible to parse vectors from a GET URL unless you choose to parse the raw string on your own?
You can get the query as a Vec<(String, String)> and go from there on your own. This way, /get_path?list=a&list=b will get the handler called with [("list", "a"), ("list", "b")]. I use this in my project rphotos:
https://github.com/kaj/rphotos/blob/77083e358058c3548ae6750257bdff881f7a948f/src/server/mod.rs#L104
https://github.com/kaj/rphotos/blob/77083e358058c3548ae6750257bdff881f7a948f/src/server/search.rs#L18-L19
https://github.com/kaj/rphotos/blob/77083e358058c3548ae6750257bdff881f7a948f/src/server/search.rs#L121-L185
A somewhat related issue is that using single checkboxes is a little bit unpleasant. If the checkbox is checked, it will be present and set to "on" (unless the value attribute is set to something else). If the checkbox is unchecked, it won't be present. This means that you need to deserialize the parameter into Option<String> rather than bool. This is probably more a serde issue but I thought I'd bring it up anyway.
Most helpful comment
@Gelox :
You can get the query as a
Vec<(String, String)>and go from there on your own. This way,/get_path?list=a&list=bwill get the handler called with[("list", "a"), ("list", "b")]. I use this in my project rphotos:https://github.com/kaj/rphotos/blob/77083e358058c3548ae6750257bdff881f7a948f/src/server/mod.rs#L104
https://github.com/kaj/rphotos/blob/77083e358058c3548ae6750257bdff881f7a948f/src/server/search.rs#L18-L19
https://github.com/kaj/rphotos/blob/77083e358058c3548ae6750257bdff881f7a948f/src/server/search.rs#L121-L185