1mod de;
5
6use crate::{
7 extract::{rejection::*, FromRequestParts},
8 routing::url_params::UrlParams,
9 util::PercentDecodedStr,
10};
11use async_trait::async_trait;
12use axum_core::response::{IntoResponse, Response};
13use http::{request::Parts, StatusCode};
14use serde::de::DeserializeOwned;
15use std::{fmt, sync::Arc};
16
17#[derive(Debug)]
144pub struct Path<T>(pub T);
145
146axum_core::__impl_deref!(Path);
147
148#[async_trait]
149impl<T, S> FromRequestParts<S> for Path<T>
150where
151 T: DeserializeOwned + Send,
152 S: Send + Sync,
153{
154 type Rejection = PathRejection;
155
156 async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
157 let params = match parts.extensions.get::<UrlParams>() {
158 Some(UrlParams::Params(params)) => params,
159 Some(UrlParams::InvalidUtf8InPathParam { key }) => {
160 let err = PathDeserializationError {
161 kind: ErrorKind::InvalidUtf8InPathParam {
162 key: key.to_string(),
163 },
164 };
165 let err = FailedToDeserializePathParams(err);
166 return Err(err.into());
167 }
168 None => {
169 return Err(MissingPathParams.into());
170 }
171 };
172
173 T::deserialize(de::PathDeserializer::new(params))
174 .map_err(|err| {
175 PathRejection::FailedToDeserializePathParams(FailedToDeserializePathParams(err))
176 })
177 .map(Path)
178 }
179}
180
181#[derive(Debug)]
184pub(crate) struct PathDeserializationError {
185 pub(super) kind: ErrorKind,
186}
187
188impl PathDeserializationError {
189 pub(super) fn new(kind: ErrorKind) -> Self {
190 Self { kind }
191 }
192
193 pub(super) fn wrong_number_of_parameters() -> WrongNumberOfParameters<()> {
194 WrongNumberOfParameters { got: () }
195 }
196
197 #[track_caller]
198 pub(super) fn unsupported_type(name: &'static str) -> Self {
199 Self::new(ErrorKind::UnsupportedType { name })
200 }
201}
202
203pub(super) struct WrongNumberOfParameters<G> {
204 got: G,
205}
206
207impl<G> WrongNumberOfParameters<G> {
208 #[allow(clippy::unused_self)]
209 pub(super) fn got<G2>(self, got: G2) -> WrongNumberOfParameters<G2> {
210 WrongNumberOfParameters { got }
211 }
212}
213
214impl WrongNumberOfParameters<usize> {
215 pub(super) fn expected(self, expected: usize) -> PathDeserializationError {
216 PathDeserializationError::new(ErrorKind::WrongNumberOfParameters {
217 got: self.got,
218 expected,
219 })
220 }
221}
222
223impl serde::de::Error for PathDeserializationError {
224 #[inline]
225 fn custom<T>(msg: T) -> Self
226 where
227 T: fmt::Display,
228 {
229 Self {
230 kind: ErrorKind::Message(msg.to_string()),
231 }
232 }
233}
234
235impl fmt::Display for PathDeserializationError {
236 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237 self.kind.fmt(f)
238 }
239}
240
241impl std::error::Error for PathDeserializationError {}
242
243#[derive(Debug, PartialEq, Eq)]
249#[non_exhaustive]
250pub enum ErrorKind {
251 WrongNumberOfParameters {
253 got: usize,
255 expected: usize,
257 },
258
259 ParseErrorAtKey {
263 key: String,
265 value: String,
267 expected_type: &'static str,
269 },
270
271 ParseErrorAtIndex {
275 index: usize,
277 value: String,
279 expected_type: &'static str,
281 },
282
283 ParseError {
287 value: String,
289 expected_type: &'static str,
291 },
292
293 InvalidUtf8InPathParam {
295 key: String,
297 },
298
299 UnsupportedType {
304 name: &'static str,
306 },
307
308 Message(String),
310}
311
312impl fmt::Display for ErrorKind {
313 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314 match self {
315 ErrorKind::Message(error) => error.fmt(f),
316 ErrorKind::InvalidUtf8InPathParam { key } => write!(f, "Invalid UTF-8 in `{key}`"),
317 ErrorKind::WrongNumberOfParameters { got, expected } => {
318 write!(
319 f,
320 "Wrong number of path arguments for `Path`. Expected {expected} but got {got}"
321 )?;
322
323 if *expected == 1 {
324 write!(f, ". Note that multiple parameters must be extracted with a tuple `Path<(_, _)>` or a struct `Path<YourParams>`")?;
325 }
326
327 Ok(())
328 }
329 ErrorKind::UnsupportedType { name } => write!(f, "Unsupported type `{name}`"),
330 ErrorKind::ParseErrorAtKey {
331 key,
332 value,
333 expected_type,
334 } => write!(
335 f,
336 "Cannot parse `{key}` with value `{value:?}` to a `{expected_type}`"
337 ),
338 ErrorKind::ParseError {
339 value,
340 expected_type,
341 } => write!(f, "Cannot parse `{value:?}` to a `{expected_type}`"),
342 ErrorKind::ParseErrorAtIndex {
343 index,
344 value,
345 expected_type,
346 } => write!(
347 f,
348 "Cannot parse value at index {index} with value `{value:?}` to a `{expected_type}`"
349 ),
350 }
351 }
352}
353
354#[derive(Debug)]
357pub struct FailedToDeserializePathParams(PathDeserializationError);
358
359impl FailedToDeserializePathParams {
360 pub fn kind(&self) -> &ErrorKind {
362 &self.0.kind
363 }
364
365 pub fn into_kind(self) -> ErrorKind {
367 self.0.kind
368 }
369
370 pub fn body_text(&self) -> String {
372 match self.0.kind {
373 ErrorKind::Message(_)
374 | ErrorKind::InvalidUtf8InPathParam { .. }
375 | ErrorKind::ParseError { .. }
376 | ErrorKind::ParseErrorAtIndex { .. }
377 | ErrorKind::ParseErrorAtKey { .. } => format!("Invalid URL: {}", self.0.kind),
378 ErrorKind::WrongNumberOfParameters { .. } | ErrorKind::UnsupportedType { .. } => {
379 self.0.kind.to_string()
380 }
381 }
382 }
383
384 pub fn status(&self) -> StatusCode {
386 match self.0.kind {
387 ErrorKind::Message(_)
388 | ErrorKind::InvalidUtf8InPathParam { .. }
389 | ErrorKind::ParseError { .. }
390 | ErrorKind::ParseErrorAtIndex { .. }
391 | ErrorKind::ParseErrorAtKey { .. } => StatusCode::BAD_REQUEST,
392 ErrorKind::WrongNumberOfParameters { .. } | ErrorKind::UnsupportedType { .. } => {
393 StatusCode::INTERNAL_SERVER_ERROR
394 }
395 }
396 }
397}
398
399impl IntoResponse for FailedToDeserializePathParams {
400 fn into_response(self) -> Response {
401 let body = self.body_text();
402 axum_core::__log_rejection!(
403 rejection_type = Self,
404 body_text = body,
405 status = self.status(),
406 );
407 (self.status(), body).into_response()
408 }
409}
410
411impl fmt::Display for FailedToDeserializePathParams {
412 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
413 self.0.fmt(f)
414 }
415}
416
417impl std::error::Error for FailedToDeserializePathParams {}
418
419#[derive(Debug)]
447pub struct RawPathParams(Vec<(Arc<str>, PercentDecodedStr)>);
448
449#[async_trait]
450impl<S> FromRequestParts<S> for RawPathParams
451where
452 S: Send + Sync,
453{
454 type Rejection = RawPathParamsRejection;
455
456 async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
457 let params = match parts.extensions.get::<UrlParams>() {
458 Some(UrlParams::Params(params)) => params,
459 Some(UrlParams::InvalidUtf8InPathParam { key }) => {
460 return Err(InvalidUtf8InPathParam {
461 key: Arc::clone(key),
462 }
463 .into());
464 }
465 None => {
466 return Err(MissingPathParams.into());
467 }
468 };
469
470 Ok(Self(params.clone()))
471 }
472}
473
474impl RawPathParams {
475 pub fn iter(&self) -> RawPathParamsIter<'_> {
477 self.into_iter()
478 }
479}
480
481impl<'a> IntoIterator for &'a RawPathParams {
482 type Item = (&'a str, &'a str);
483 type IntoIter = RawPathParamsIter<'a>;
484
485 fn into_iter(self) -> Self::IntoIter {
486 RawPathParamsIter(self.0.iter())
487 }
488}
489
490#[derive(Debug)]
494pub struct RawPathParamsIter<'a>(std::slice::Iter<'a, (Arc<str>, PercentDecodedStr)>);
495
496impl<'a> Iterator for RawPathParamsIter<'a> {
497 type Item = (&'a str, &'a str);
498
499 fn next(&mut self) -> Option<Self::Item> {
500 let (key, value) = self.0.next()?;
501 Some((&**key, value.as_str()))
502 }
503}
504
505#[derive(Debug)]
508pub struct InvalidUtf8InPathParam {
509 key: Arc<str>,
510}
511
512impl InvalidUtf8InPathParam {
513 pub fn body_text(&self) -> String {
515 self.to_string()
516 }
517
518 pub fn status(&self) -> StatusCode {
520 StatusCode::BAD_REQUEST
521 }
522}
523
524impl fmt::Display for InvalidUtf8InPathParam {
525 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
526 write!(f, "Invalid UTF-8 in `{}`", self.key)
527 }
528}
529
530impl std::error::Error for InvalidUtf8InPathParam {}
531
532impl IntoResponse for InvalidUtf8InPathParam {
533 fn into_response(self) -> Response {
534 let body = self.body_text();
535 axum_core::__log_rejection!(
536 rejection_type = Self,
537 body_text = body,
538 status = self.status(),
539 );
540 (self.status(), body).into_response()
541 }
542}
543
544#[cfg(test)]
545mod tests {
546 use super::*;
547 use crate::{routing::get, test_helpers::*, Router};
548 use serde::Deserialize;
549 use std::collections::HashMap;
550
551 #[crate::test]
552 async fn extracting_url_params() {
553 let app = Router::new().route(
554 "/users/:id",
555 get(|Path(id): Path<i32>| async move {
556 assert_eq!(id, 42);
557 })
558 .post(|Path(params_map): Path<HashMap<String, i32>>| async move {
559 assert_eq!(params_map.get("id").unwrap(), &1337);
560 }),
561 );
562
563 let client = TestClient::new(app);
564
565 let res = client.get("/users/42").await;
566 assert_eq!(res.status(), StatusCode::OK);
567
568 let res = client.post("/users/1337").await;
569 assert_eq!(res.status(), StatusCode::OK);
570 }
571
572 #[crate::test]
573 async fn extracting_url_params_multiple_times() {
574 let app = Router::new().route("/users/:id", get(|_: Path<i32>, _: Path<String>| async {}));
575
576 let client = TestClient::new(app);
577
578 let res = client.get("/users/42").await;
579 assert_eq!(res.status(), StatusCode::OK);
580 }
581
582 #[crate::test]
583 async fn percent_decoding() {
584 let app = Router::new().route(
585 "/:key",
586 get(|Path(param): Path<String>| async move { param }),
587 );
588
589 let client = TestClient::new(app);
590
591 let res = client.get("/one%20two").await;
592
593 assert_eq!(res.text().await, "one two");
594 }
595
596 #[crate::test]
597 async fn supports_128_bit_numbers() {
598 let app = Router::new()
599 .route(
600 "/i/:key",
601 get(|Path(param): Path<i128>| async move { param.to_string() }),
602 )
603 .route(
604 "/u/:key",
605 get(|Path(param): Path<u128>| async move { param.to_string() }),
606 );
607
608 let client = TestClient::new(app);
609
610 let res = client.get("/i/123").await;
611 assert_eq!(res.text().await, "123");
612
613 let res = client.get("/u/123").await;
614 assert_eq!(res.text().await, "123");
615 }
616
617 #[crate::test]
618 async fn wildcard() {
619 let app = Router::new()
620 .route(
621 "/foo/*rest",
622 get(|Path(param): Path<String>| async move { param }),
623 )
624 .route(
625 "/bar/*rest",
626 get(|Path(params): Path<HashMap<String, String>>| async move {
627 params.get("rest").unwrap().clone()
628 }),
629 );
630
631 let client = TestClient::new(app);
632
633 let res = client.get("/foo/bar/baz").await;
634 assert_eq!(res.text().await, "bar/baz");
635
636 let res = client.get("/bar/baz/qux").await;
637 assert_eq!(res.text().await, "baz/qux");
638 }
639
640 #[crate::test]
641 async fn captures_dont_match_empty_path() {
642 let app = Router::new().route("/:key", get(|| async {}));
643
644 let client = TestClient::new(app);
645
646 let res = client.get("/").await;
647 assert_eq!(res.status(), StatusCode::NOT_FOUND);
648
649 let res = client.get("/foo").await;
650 assert_eq!(res.status(), StatusCode::OK);
651 }
652
653 #[crate::test]
654 async fn captures_match_empty_inner_segments() {
655 let app = Router::new().route(
656 "/:key/method",
657 get(|Path(param): Path<String>| async move { param.to_string() }),
658 );
659
660 let client = TestClient::new(app);
661
662 let res = client.get("/abc/method").await;
663 assert_eq!(res.text().await, "abc");
664
665 let res = client.get("//method").await;
666 assert_eq!(res.text().await, "");
667 }
668
669 #[crate::test]
670 async fn captures_match_empty_inner_segments_near_end() {
671 let app = Router::new().route(
672 "/method/:key/",
673 get(|Path(param): Path<String>| async move { param.to_string() }),
674 );
675
676 let client = TestClient::new(app);
677
678 let res = client.get("/method/abc").await;
679 assert_eq!(res.status(), StatusCode::NOT_FOUND);
680
681 let res = client.get("/method/abc/").await;
682 assert_eq!(res.text().await, "abc");
683
684 let res = client.get("/method//").await;
685 assert_eq!(res.text().await, "");
686 }
687
688 #[crate::test]
689 async fn captures_match_empty_trailing_segment() {
690 let app = Router::new().route(
691 "/method/:key",
692 get(|Path(param): Path<String>| async move { param.to_string() }),
693 );
694
695 let client = TestClient::new(app);
696
697 let res = client.get("/method/abc/").await;
698 assert_eq!(res.status(), StatusCode::NOT_FOUND);
699
700 let res = client.get("/method/abc").await;
701 assert_eq!(res.text().await, "abc");
702
703 let res = client.get("/method/").await;
704 assert_eq!(res.text().await, "");
705
706 let res = client.get("/method").await;
707 assert_eq!(res.status(), StatusCode::NOT_FOUND);
708 }
709
710 #[crate::test]
711 async fn str_reference_deserialize() {
712 struct Param(String);
713 impl<'de> serde::Deserialize<'de> for Param {
714 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
715 where
716 D: serde::Deserializer<'de>,
717 {
718 let s = <&str as serde::Deserialize>::deserialize(deserializer)?;
719 Ok(Param(s.to_owned()))
720 }
721 }
722
723 let app = Router::new().route("/:key", get(|param: Path<Param>| async move { param.0 .0 }));
724
725 let client = TestClient::new(app);
726
727 let res = client.get("/foo").await;
728 assert_eq!(res.text().await, "foo");
729
730 let res = client.get("/foo%20bar").await;
732 assert_eq!(res.text().await, "foo bar");
733 }
734
735 #[crate::test]
736 async fn two_path_extractors() {
737 let app = Router::new().route("/:a/:b", get(|_: Path<String>, _: Path<String>| async {}));
738
739 let client = TestClient::new(app);
740
741 let res = client.get("/a/b").await;
742 assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);
743 assert_eq!(
744 res.text().await,
745 "Wrong number of path arguments for `Path`. Expected 1 but got 2. \
746 Note that multiple parameters must be extracted with a tuple `Path<(_, _)>` or a struct `Path<YourParams>`",
747 );
748 }
749
750 #[crate::test]
751 async fn deserialize_into_vec_of_tuples() {
752 let app = Router::new().route(
753 "/:a/:b",
754 get(|Path(params): Path<Vec<(String, String)>>| async move {
755 assert_eq!(
756 params,
757 vec![
758 ("a".to_owned(), "foo".to_owned()),
759 ("b".to_owned(), "bar".to_owned())
760 ]
761 );
762 }),
763 );
764
765 let client = TestClient::new(app);
766
767 let res = client.get("/foo/bar").await;
768 assert_eq!(res.status(), StatusCode::OK);
769 }
770
771 #[crate::test]
772 async fn type_that_uses_deserialize_any() {
773 use time::Date;
774
775 #[derive(Deserialize)]
776 struct Params {
777 a: Date,
778 b: Date,
779 c: Date,
780 }
781
782 let app = Router::new()
783 .route(
784 "/single/:a",
785 get(|Path(a): Path<Date>| async move { format!("single: {a}") }),
786 )
787 .route(
788 "/tuple/:a/:b/:c",
789 get(|Path((a, b, c)): Path<(Date, Date, Date)>| async move {
790 format!("tuple: {a} {b} {c}")
791 }),
792 )
793 .route(
794 "/vec/:a/:b/:c",
795 get(|Path(vec): Path<Vec<Date>>| async move {
796 let [a, b, c]: [Date; 3] = vec.try_into().unwrap();
797 format!("vec: {a} {b} {c}")
798 }),
799 )
800 .route(
801 "/vec_pairs/:a/:b/:c",
802 get(|Path(vec): Path<Vec<(String, Date)>>| async move {
803 let [(_, a), (_, b), (_, c)]: [(String, Date); 3] = vec.try_into().unwrap();
804 format!("vec_pairs: {a} {b} {c}")
805 }),
806 )
807 .route(
808 "/map/:a/:b/:c",
809 get(|Path(mut map): Path<HashMap<String, Date>>| async move {
810 let a = map.remove("a").unwrap();
811 let b = map.remove("b").unwrap();
812 let c = map.remove("c").unwrap();
813 format!("map: {a} {b} {c}")
814 }),
815 )
816 .route(
817 "/struct/:a/:b/:c",
818 get(|Path(params): Path<Params>| async move {
819 format!("struct: {} {} {}", params.a, params.b, params.c)
820 }),
821 );
822
823 let client = TestClient::new(app);
824
825 let res = client.get("/single/2023-01-01").await;
826 assert_eq!(res.text().await, "single: 2023-01-01");
827
828 let res = client.get("/tuple/2023-01-01/2023-01-02/2023-01-03").await;
829 assert_eq!(res.text().await, "tuple: 2023-01-01 2023-01-02 2023-01-03");
830
831 let res = client.get("/vec/2023-01-01/2023-01-02/2023-01-03").await;
832 assert_eq!(res.text().await, "vec: 2023-01-01 2023-01-02 2023-01-03");
833
834 let res = client
835 .get("/vec_pairs/2023-01-01/2023-01-02/2023-01-03")
836 .await;
837 assert_eq!(
838 res.text().await,
839 "vec_pairs: 2023-01-01 2023-01-02 2023-01-03",
840 );
841
842 let res = client.get("/map/2023-01-01/2023-01-02/2023-01-03").await;
843 assert_eq!(res.text().await, "map: 2023-01-01 2023-01-02 2023-01-03");
844
845 let res = client.get("/struct/2023-01-01/2023-01-02/2023-01-03").await;
846 assert_eq!(res.text().await, "struct: 2023-01-01 2023-01-02 2023-01-03");
847 }
848
849 #[crate::test]
850 async fn wrong_number_of_parameters_json() {
851 use serde_json::Value;
852
853 let app = Router::new()
854 .route("/one/:a", get(|_: Path<(Value, Value)>| async {}))
855 .route("/two/:a/:b", get(|_: Path<Value>| async {}));
856
857 let client = TestClient::new(app);
858
859 let res = client.get("/one/1").await;
860 assert!(res
861 .text()
862 .await
863 .starts_with("Wrong number of path arguments for `Path`. Expected 2 but got 1"));
864
865 let res = client.get("/two/1/2").await;
866 assert!(res
867 .text()
868 .await
869 .starts_with("Wrong number of path arguments for `Path`. Expected 1 but got 2"));
870 }
871
872 #[crate::test]
873 async fn raw_path_params() {
874 let app = Router::new().route(
875 "/:a/:b/:c",
876 get(|params: RawPathParams| async move {
877 params
878 .into_iter()
879 .map(|(key, value)| format!("{key}={value}"))
880 .collect::<Vec<_>>()
881 .join(" ")
882 }),
883 );
884
885 let client = TestClient::new(app);
886 let res = client.get("/foo/bar/baz").await;
887 let body = res.text().await;
888 assert_eq!(body, "a=foo b=bar c=baz");
889 }
890}