mirror of
https://github.com/tokio-rs/axum.git
synced 2026-08-25 00:00:23 +02:00
99 lines
2.6 KiB
Rust
99 lines
2.6 KiB
Rust
use axum::{
|
|
extract::{rejection::PathRejection, FromRequestParts, Path},
|
|
RequestPartsExt,
|
|
};
|
|
use serde_core::de::DeserializeOwned;
|
|
|
|
/// Extractor that extracts path arguments the same way as [`Path`], except if there aren't any.
|
|
///
|
|
/// This extractor can be used in place of `Path` when you have two routes that you want to handle
|
|
/// in mostly the same way, where one has a path parameter and the other one doesn't.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// use std::num::NonZeroU32;
|
|
/// use axum::{
|
|
/// response::IntoResponse,
|
|
/// routing::get,
|
|
/// Router,
|
|
/// };
|
|
/// use axum_extra::extract::OptionalPath;
|
|
///
|
|
/// async fn render_blog(OptionalPath(page): OptionalPath<NonZeroU32>) -> impl IntoResponse {
|
|
/// // Convert to u32, default to page 1 if not specified
|
|
/// let page = page.map_or(1, |param| param.get());
|
|
/// // ...
|
|
/// }
|
|
///
|
|
/// let app = Router::new()
|
|
/// .route("/blog", get(render_blog))
|
|
/// .route("/blog/{page}", get(render_blog));
|
|
/// # let app: Router = app;
|
|
/// ```
|
|
#[deprecated = "Use Option<Path<_>> instead"]
|
|
#[derive(Debug)]
|
|
pub struct OptionalPath<T>(pub Option<T>);
|
|
|
|
#[allow(deprecated)]
|
|
impl<T, S> FromRequestParts<S> for OptionalPath<T>
|
|
where
|
|
T: DeserializeOwned + Send + 'static,
|
|
S: Send + Sync,
|
|
{
|
|
type Rejection = PathRejection;
|
|
|
|
async fn from_request_parts(
|
|
parts: &mut http::request::Parts,
|
|
_: &S,
|
|
) -> Result<Self, Self::Rejection> {
|
|
parts
|
|
.extract::<Option<Path<T>>>()
|
|
.await
|
|
.map(|opt| Self(opt.map(|Path(x)| x)))
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[allow(deprecated)]
|
|
mod tests {
|
|
use std::num::NonZeroU32;
|
|
|
|
use axum::{routing::get, Router};
|
|
|
|
use super::OptionalPath;
|
|
use crate::test_helpers::TestClient;
|
|
|
|
#[crate::test]
|
|
async fn supports_128_bit_numbers() {
|
|
async fn handle(OptionalPath(param): OptionalPath<NonZeroU32>) -> String {
|
|
let num = param.map_or(0, |p| p.get());
|
|
format!("Success: {num}")
|
|
}
|
|
|
|
let app = Router::new()
|
|
.route("/", get(handle))
|
|
.route("/{num}", get(handle));
|
|
|
|
let client = TestClient::new(app);
|
|
|
|
let res = client.get("/").await;
|
|
assert_eq!(res.text().await, "Success: 0");
|
|
|
|
let res = client.get("/1").await;
|
|
assert_eq!(res.text().await, "Success: 1");
|
|
|
|
let res = client.get("/0").await;
|
|
assert_eq!(
|
|
res.text().await,
|
|
"Invalid URL: invalid value: integer `0`, expected a nonzero u32"
|
|
);
|
|
|
|
let res = client.get("/NaN").await;
|
|
assert_eq!(
|
|
res.text().await,
|
|
"Invalid URL: Cannot parse `NaN` to a `u32`"
|
|
);
|
|
}
|
|
}
|