gotham/extractor/
query_string.rs

1use hyper::body::HttpBody;
2use hyper::{Body, Response};
3use serde::{Deserialize, Deserializer};
4
5use crate::router::response::StaticResponseExtender;
6use crate::state::{State, StateData};
7
8/// Defines a binding for storing the query parameters from the `Request` URI in `State`. On
9/// failure the `StaticResponseExtender` implementation extends the `Response` to indicate why the
10/// extraction process failed.
11///
12/// This trait is automatically implemented when the struct implements the `Deserialize`,
13/// `StateData` and `StaticResponseExtender` traits. These traits can be derived, or implemented
14/// manually for greater control.
15///
16/// The default behaviour given by deriving all three traits will use the automatically derived
17/// behaviour from Serde, and result in a `400 Bad Request` HTTP response if the query string is
18/// not able to be deserialized.
19///
20/// # Examples
21///
22/// ```rust
23/// # use hyper::{Body, Response, StatusCode};
24/// # use gotham::state::State;
25/// # use gotham::helpers::http::response::create_response;
26/// # use gotham::router::{build_simple_router, Router};
27/// # use gotham::prelude::*;
28/// # use gotham::test::TestServer;
29/// # use serde::Deserialize;
30/// #
31/// #[derive(Deserialize, StateData, StaticResponseExtender)]
32/// struct MyQueryParams {
33///     x: i32,
34///     y: MyEnum,
35/// }
36///
37/// #[derive(Deserialize, Clone, Copy, Debug)]
38/// #[serde(rename_all = "kebab-case")]
39/// enum MyEnum {
40///     A,
41///     B,
42///     C,
43/// }
44///
45/// fn handler(state: State) -> (State, Response<Body>) {
46///     let &MyQueryParams { x, y } = MyQueryParams::borrow_from(&state);
47///     let body = format!("x = {}, y = {:?}", x, y);
48///
49///     let response = create_response(
50///         &state,
51///         StatusCode::OK,
52///         mime::TEXT_PLAIN,
53///         body,
54///     );
55///
56///     (state, response)
57/// }
58///
59/// fn router() -> Router {
60///     build_simple_router(|route| {
61///         route
62///             .get("/test")
63///             .with_query_string_extractor::<MyQueryParams>()
64///             .to(handler);
65///     })
66/// }
67/// #
68/// # fn main() {
69/// #   let test_server = TestServer::new(router()).unwrap();
70/// #   let response = test_server
71/// #       .client()
72/// #       .get("http://example.com/test?x=15&y=b")
73/// #       .perform()
74/// #       .unwrap();
75/// #   assert_eq!(response.status(), StatusCode::OK);
76/// #   let body = response.read_utf8_body().unwrap();
77/// #   assert_eq!(body, "x = 15, y = B");
78/// # }
79pub trait QueryStringExtractor<B>:
80    for<'de> Deserialize<'de> + StaticResponseExtender<ResBody = B> + StateData
81where
82    B: HttpBody,
83{
84}
85
86impl<T, B> QueryStringExtractor<B> for T
87where
88    B: HttpBody,
89    for<'de> T: Deserialize<'de> + StaticResponseExtender<ResBody = B> + StateData,
90{
91}
92
93/// A `QueryStringExtractor` that does not extract/store any data.
94///
95/// This is the default `QueryStringExtractor` which is applied to a route when no other
96/// `QueryStringExtractor` is provided. It ignores any query parameters, and always succeeds during
97/// deserialization.
98#[derive(Debug)]
99pub struct NoopQueryStringExtractor;
100
101// This doesn't get derived correctly if we just `#[derive(Deserialize)]` above, because the
102// Deserializer expects to _ignore_ a value, not just do nothing. By filling in the impl ourselves,
103// we can explicitly do nothing.
104impl<'de> Deserialize<'de> for NoopQueryStringExtractor {
105    fn deserialize<D>(_de: D) -> Result<Self, D::Error>
106    where
107        D: Deserializer<'de>,
108    {
109        Ok(NoopQueryStringExtractor)
110    }
111}
112
113impl StateData for NoopQueryStringExtractor {}
114
115impl StaticResponseExtender for NoopQueryStringExtractor {
116    type ResBody = Body;
117    fn extend(_state: &mut State, _res: &mut Response<Body>) {}
118}