mirror of
https://github.com/tokio-rs/axum.git
synced 2025-03-27 00:48:44 +01:00
Typed url param extractor
This commit is contained in:
parent
03fb15e7a7
commit
0b2f791bf4
3 changed files with 69 additions and 27 deletions
|
@ -52,15 +52,15 @@ struct State {
|
|||
|
||||
async fn get(
|
||||
_req: Request<Body>,
|
||||
params: extract::UrlParamsMap,
|
||||
params: extract::UrlParams<(String,)>,
|
||||
state: extract::Extension<SharedState>,
|
||||
) -> Result<Bytes, Error> {
|
||||
let state = state.into_inner();
|
||||
let db = &state.lock().unwrap().db;
|
||||
|
||||
let key = params.get("key")?;
|
||||
let (key,) = params.into_inner();
|
||||
|
||||
if let Some(value) = db.get(key) {
|
||||
if let Some(value) = db.get(&key) {
|
||||
Ok(value.clone())
|
||||
} else {
|
||||
Err(Error::Status(StatusCode::NOT_FOUND))
|
||||
|
@ -69,14 +69,14 @@ async fn get(
|
|||
|
||||
async fn set(
|
||||
_req: Request<Body>,
|
||||
params: extract::UrlParamsMap,
|
||||
params: extract::UrlParams<(String,)>,
|
||||
value: extract::BytesMaxLength<{ 1024 * 5_000 }>, // ~5mb
|
||||
state: extract::Extension<SharedState>,
|
||||
) -> Result<response::Empty, Error> {
|
||||
let state = state.into_inner();
|
||||
let db = &mut state.lock().unwrap().db;
|
||||
|
||||
let key = params.get("key")?;
|
||||
let (key,) = params.into_inner();
|
||||
let value = value.into_inner();
|
||||
|
||||
db.insert(key.to_string(), value);
|
||||
|
|
|
@ -197,7 +197,7 @@ impl UrlParamsMap {
|
|||
|
||||
pub fn get_typed<T>(&self, key: &str) -> Result<T, Error>
|
||||
where
|
||||
T: std::str::FromStr,
|
||||
T: FromStr,
|
||||
{
|
||||
self.get(key)?.parse().map_err(|_| Error::InvalidUrlParam {
|
||||
type_name: std::any::type_name::<T>(),
|
||||
|
@ -220,3 +220,65 @@ impl FromRequest for UrlParamsMap {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UrlParams<T>(T);
|
||||
|
||||
impl<T> UrlParams<T> {
|
||||
pub fn into_inner(self) -> T {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_parse_url {
|
||||
() => {};
|
||||
|
||||
( $head:ident, $($tail:ident),* $(,)? ) => {
|
||||
impl<$head, $($tail,)*> FromRequest for UrlParams<($head, $($tail,)*)>
|
||||
where
|
||||
$head: FromStr + Send,
|
||||
$( $tail: FromStr + Send, )*
|
||||
{
|
||||
type Future = future::Ready<Result<Self, Error>>;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn from_request(req: &mut Request<Body>) -> Self::Future {
|
||||
let params = if let Some(params) = req
|
||||
.extensions_mut()
|
||||
.get_mut::<Option<crate::routing::UrlParams>>()
|
||||
{
|
||||
params.take().expect("params already taken").0
|
||||
} else {
|
||||
panic!("no url params found for matched route. This is a bug in tower-web")
|
||||
};
|
||||
|
||||
if let [(_, $head), $((_, $tail),)*] = &*params {
|
||||
let $head = if let Ok(x) = $head.parse::<$head>() {
|
||||
x
|
||||
} else {
|
||||
return future::err(Error::InvalidUrlParam {
|
||||
type_name: std::any::type_name::<$head>(),
|
||||
});
|
||||
};
|
||||
|
||||
$(
|
||||
let $tail = if let Ok(x) = $tail.parse::<$tail>() {
|
||||
x
|
||||
} else {
|
||||
return future::err(Error::InvalidUrlParam {
|
||||
type_name: std::any::type_name::<$tail>(),
|
||||
});
|
||||
};
|
||||
)*
|
||||
|
||||
future::ok(UrlParams(($head, $($tail,)*)))
|
||||
} else {
|
||||
panic!("wrong number of url params found for matched route. This is a bug in tower-web")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_parse_url!($($tail,)*);
|
||||
};
|
||||
}
|
||||
|
||||
impl_parse_url!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16);
|
||||
|
|
|
@ -51,27 +51,7 @@ where
|
|||
}
|
||||
|
||||
macro_rules! impl_handler {
|
||||
( $head:ident $(,)? ) => {
|
||||
#[async_trait]
|
||||
#[allow(non_snake_case)]
|
||||
impl<F, Fut, B, Res, $head> Handler<B, ($head,)> for F
|
||||
where
|
||||
F: Fn(Request<Body>, $head) -> Fut + Send + Sync,
|
||||
Fut: Future<Output = Result<Res, Error>> + Send,
|
||||
Res: IntoResponse<B>,
|
||||
$head: FromRequest + Send,
|
||||
{
|
||||
type Response = Res;
|
||||
|
||||
type Sealed = sealed::Hidden;
|
||||
|
||||
async fn call(self, mut req: Request<Body>) -> Result<Self::Response, Error> {
|
||||
let $head = $head::from_request(&mut req).await?;
|
||||
let res = self(req, $head).await?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
};
|
||||
() => {};
|
||||
|
||||
( $head:ident, $($tail:ident),* $(,)? ) => {
|
||||
#[async_trait]
|
||||
|
|
Loading…
Add table
Reference in a new issue