//! Path extractor use std::{fmt, ops}; use actix_http::error::{Error, ErrorNotFound}; use actix_router::PathDeserializer; use serde::de; use crate::dev::Payload; use crate::request::HttpRequest; use crate::FromRequest; #[derive(PartialEq, Eq, PartialOrd, Ord)] /// Extract typed information from the request's path. /// /// ## Example /// /// ```rust /// use actix_web::{web, App}; /// /// /// extract path info from "/{username}/{count}/index.html" url /// /// {username} - deserializes to a String /// /// {count} - - deserializes to a u32 /// fn index(info: web::Path<(String, u32)>) -> String { /// format!("Welcome {}! {}", info.0, info.1) /// } /// /// fn main() { /// let app = App::new().service( /// web::resource("/{username}/{count}/index.html") // <- define path parameters /// .route(web::get().to(index)) // <- register handler with `Path` extractor /// ); /// } /// ``` /// /// It is possible to extract path information to a specific type that /// implements `Deserialize` trait from *serde*. /// /// ```rust /// #[macro_use] extern crate serde_derive; /// use actix_web::{web, App, Error}; /// /// #[derive(Deserialize)] /// struct Info { /// username: String, /// } /// /// /// extract `Info` from a path using serde /// fn index(info: web::Path) -> Result { /// Ok(format!("Welcome {}!", info.username)) /// } /// /// fn main() { /// let app = App::new().service( /// web::resource("/{username}/index.html") // <- define path parameters /// .route(web::get().to(index)) // <- use handler with Path` extractor /// ); /// } /// ``` pub struct Path { inner: T, } impl Path { /// Deconstruct to an inner value pub fn into_inner(self) -> T { self.inner } } impl AsRef for Path { fn as_ref(&self) -> &T { &self.inner } } impl ops::Deref for Path { type Target = T; fn deref(&self) -> &T { &self.inner } } impl ops::DerefMut for Path { fn deref_mut(&mut self) -> &mut T { &mut self.inner } } impl From for Path { fn from(inner: T) -> Path { Path { inner } } } impl fmt::Debug for Path { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.inner.fmt(f) } } impl fmt::Display for Path { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.inner.fmt(f) } } /// Extract typed information from the request's path. /// /// ## Example /// /// ```rust /// use actix_web::{web, App}; /// /// /// extract path info from "/{username}/{count}/index.html" url /// /// {username} - deserializes to a String /// /// {count} - - deserializes to a u32 /// fn index(info: web::Path<(String, u32)>) -> String { /// format!("Welcome {}! {}", info.0, info.1) /// } /// /// fn main() { /// let app = App::new().service( /// web::resource("/{username}/{count}/index.html") // <- define path parameters /// .route(web::get().to(index)) // <- register handler with `Path` extractor /// ); /// } /// ``` /// /// It is possible to extract path information to a specific type that /// implements `Deserialize` trait from *serde*. /// /// ```rust /// #[macro_use] extern crate serde_derive; /// use actix_web::{web, App, Error}; /// /// #[derive(Deserialize)] /// struct Info { /// username: String, /// } /// /// /// extract `Info` from a path using serde /// fn index(info: web::Path) -> Result { /// Ok(format!("Welcome {}!", info.username)) /// } /// /// fn main() { /// let app = App::new().service( /// web::resource("/{username}/index.html") // <- define path parameters /// .route(web::get().to(index)) // <- use handler with Path` extractor /// ); /// } /// ``` impl FromRequest for Path where T: de::DeserializeOwned, { type Error = Error; type Future = Result; #[inline] fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { de::Deserialize::deserialize(PathDeserializer::new(req.match_info())) .map(|inner| Path { inner }) .map_err(ErrorNotFound) } } #[cfg(test)] mod tests { use actix_router::ResourceDef; use super::*; use crate::test::{block_on, TestRequest}; #[test] fn test_extract_path_single() { let resource = ResourceDef::new("/{value}/"); let mut req = TestRequest::with_uri("/32/").to_srv_request(); resource.match_path(req.match_info_mut()); let (req, mut pl) = req.into_parts(); assert_eq!(*Path::::from_request(&req, &mut pl).unwrap(), 32); } #[test] fn test_tuple_extract() { let resource = ResourceDef::new("/{key}/{value}/"); let mut req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request(); resource.match_path(req.match_info_mut()); let (req, mut pl) = req.into_parts(); let res = block_on(<(Path<(String, String)>,)>::from_request(&req, &mut pl)).unwrap(); assert_eq!((res.0).0, "name"); assert_eq!((res.0).1, "user1"); let res = block_on( <(Path<(String, String)>, Path<(String, String)>)>::from_request( &req, &mut pl, ), ) .unwrap(); assert_eq!((res.0).0, "name"); assert_eq!((res.0).1, "user1"); assert_eq!((res.1).0, "name"); assert_eq!((res.1).1, "user1"); let () = <()>::from_request(&req, &mut pl).unwrap(); } }