## Issues Addressed Closes #2739 Closes #2812 ## Proposed Changes Support the deserialization of query strings containing duplicate keys into their corresponding types. As `warp` does not support this feature natively (as discussed in #2739), it relies on the external library [`serde_array_query`](https://github.com/sigp/serde_array_query) (written by @michaelsproul) This is backwards compatible meaning that both of the following requests will produce the same output: ``` curl "http://localhost:5052/eth/v1/events?topics=head,block" ``` ``` curl "http://localhost:5052/eth/v1/events?topics=head&topics=block" ``` ## Additional Info Certain error messages have changed slightly. This only affects endpoints which accept multiple values. For example: ``` {"code":400,"message":"BAD_REQUEST: invalid query: Invalid query string","stacktraces":[]} ``` is now ``` {"code":400,"message":"BAD_REQUEST: unable to parse query","stacktraces":[]} ``` The serve order of the endpoints `get_beacon_state_validators` and `get_beacon_state_validators_id` have flipped: ```rust .or(get_beacon_state_validators_id.boxed()) .or(get_beacon_state_validators.boxed()) ``` This is to ensure proper error messages when filter fallback occurs due to the use of the `and_then` filter. ## Future Work - Cleanup / remove filter fallback behaviour by substituting `and_then` with `then` where appropriate. - Add regression tests for HTTP API error messages. ## Credits - @mooori for doing the ground work of investigating possible solutions within the existing Rust ecosystem. - @michaelsproul for writing [`serde_array_query`](https://github.com/sigp/serde_array_query) and for helping debug the behaviour of the `warp` filter fallback leading to incorrect error messages.
23 lines
866 B
Rust
23 lines
866 B
Rust
use crate::reject::custom_bad_request;
|
|
use serde::Deserialize;
|
|
use warp::Filter;
|
|
|
|
// Custom query filter using `serde_array_query`.
|
|
// This allows duplicate keys inside query strings.
|
|
pub fn multi_key_query<'de, T: Deserialize<'de>>(
|
|
) -> impl warp::Filter<Extract = (Result<T, warp::Rejection>,), Error = std::convert::Infallible> + Copy
|
|
{
|
|
raw_query().then(|query_str: String| async move {
|
|
serde_array_query::from_str(&query_str).map_err(|e| custom_bad_request(e.to_string()))
|
|
})
|
|
}
|
|
|
|
// This ensures that empty query strings are still accepted.
|
|
// This is because warp::filters::query::raw() does not allow empty query strings
|
|
// but warp::query::<T>() does.
|
|
fn raw_query() -> impl Filter<Extract = (String,), Error = std::convert::Infallible> + Copy {
|
|
warp::filters::query::raw()
|
|
.or(warp::any().map(String::default))
|
|
.unify()
|
|
}
|