diff --git a/src/app.rs b/src/app.rs index c9c23d9cb..34a663ae7 100644 --- a/src/app.rs +++ b/src/app.rs @@ -245,8 +245,8 @@ where } } -/// Structure that follows the builder pattern for building application -/// instances. +/// Application router builder - Structure that follows the builder pattern +/// for building application instances. pub struct AppRouter { chain: C, services: Vec<(ResourceDef, HttpNewService

)>, diff --git a/src/extract.rs b/src/extract.rs index d6e533276..c0af6016d 100644 --- a/src/extract.rs +++ b/src/extract.rs @@ -165,17 +165,63 @@ impl From for Path { } } +/// Extract typed information from the request's path. +/// +/// ## Example +/// +/// ```rust +/// use actix_web::{web, http, App, extract::Path}; +/// +/// /// extract path info from "/{username}/{count}/index.html" url +/// /// {username} - deserializes to a String +/// /// {count} - - deserializes to a u32 +/// fn index(info: Path<(String, u32)>) -> String { +/// format!("Welcome {}! {}", info.0, info.1) +/// } +/// +/// fn main() { +/// let app = App::new().resource( +/// "/{username}/{count}/index.html", // <- define path parameters +/// |r| r.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, extract::Path, Error}; +/// +/// #[derive(Deserialize)] +/// struct Info { +/// username: String, +/// } +/// +/// /// extract `Info` from a path using serde +/// fn index(info: Path) -> Result { +/// Ok(format!("Welcome {}!", info.username)) +/// } +/// +/// fn main() { +/// let app = App::new().resource( +/// "/{username}/index.html", // <- define path parameters +/// |r| r.route(web::get().to(index)) // <- use handler with Path` extractor +/// ); +/// } +/// ``` impl FromRequest

for Path where T: DeserializeOwned, { type Error = Error; - type Future = FutureResult; + type Future = Result; type Config = (); #[inline] fn from_request(req: &mut ServiceFromRequest

) -> Self::Future { - Self::extract(req).map_err(ErrorNotFound).into_future() + Self::extract(req).map_err(ErrorNotFound) } } @@ -200,17 +246,17 @@ impl fmt::Display for Path { /// #[macro_use] extern crate serde_derive; /// use actix_web::{web, extract, App}; /// -///#[derive(Debug, Deserialize)] -///pub enum ResponseType { +/// #[derive(Debug, Deserialize)] +/// pub enum ResponseType { /// Token, /// Code -///} +/// } /// -///#[derive(Deserialize)] -///pub struct AuthRequest { +/// #[derive(Deserialize)] +/// pub struct AuthRequest { /// id: u64, /// response_type: ResponseType, -///} +/// } /// /// // Use `Query` extractor for query information. /// // This handler get called only if request's query contains `username` field @@ -248,19 +294,52 @@ impl Query { } } +/// Extract typed information from from the request's query. +/// +/// ## Example +/// +/// ```rust +/// #[macro_use] extern crate serde_derive; +/// use actix_web::{web, extract, App}; +/// +/// #[derive(Debug, Deserialize)] +/// pub enum ResponseType { +/// Token, +/// Code +/// } +/// +/// #[derive(Deserialize)] +/// pub struct AuthRequest { +/// id: u64, +/// response_type: ResponseType, +/// } +/// +/// // Use `Query` extractor for query information. +/// // This handler get called only if request's query contains `username` field +/// // The correct request for this handler would be `/index.html?id=64&response_type=Code"` +/// fn index(info: extract::Query) -> String { +/// format!("Authorization request for client with id={} and type={:?}!", info.id, info.response_type) +/// } +/// +/// fn main() { +/// let app = App::new().resource( +/// "/index.html", +/// |r| r.route(web::get().to(index))); // <- use `Query` extractor +/// } +/// ``` impl FromRequest

for Query where T: de::DeserializeOwned, { type Error = Error; - type Future = FutureResult; + type Future = Result; type Config = (); #[inline] fn from_request(req: &mut ServiceFromRequest

) -> Self::Future { serde_urlencoded::from_str::(req.query_string()) - .map(|val| ok(Query(val))) - .unwrap_or_else(|e| err(e.into())) + .map(|val| Ok(Query(val))) + .unwrap_or_else(|e| Err(e.into())) } } @@ -282,7 +361,7 @@ impl fmt::Display for Query { /// To extract typed information from request's body, the type `T` must /// implement the `Deserialize` trait from *serde*. /// -/// [**FormConfig**](dev/struct.FormConfig.html) allows to configure extraction +/// [**FormConfig**](struct.FormConfig.html) allows to configure extraction /// process. /// /// ## Example @@ -436,7 +515,7 @@ impl Default for FormConfig { /// To extract typed information from request's body, the type `T` must /// implement the `Deserialize` trait from *serde*. /// -/// [**JsonConfig**](dev/struct.JsonConfig.html) allows to configure extraction +/// [**JsonConfig**](struct.JsonConfig.html) allows to configure extraction /// process. /// /// ## Example @@ -526,20 +605,51 @@ where impl Responder for Json { type Error = Error; - type Future = FutureResult; + type Future = Result; fn respond_to(self, _: &HttpRequest) -> Self::Future { let body = match serde_json::to_string(&self.0) { Ok(body) => body, - Err(e) => return err(e.into()), + Err(e) => return Err(e.into()), }; - ok(Response::build(StatusCode::OK) + Ok(Response::build(StatusCode::OK) .content_type("application/json") .body(body)) } } +/// Json extractor. Allow to extract typed information from request's +/// payload. +/// +/// To extract typed information from request's body, the type `T` must +/// implement the `Deserialize` trait from *serde*. +/// +/// [**JsonConfig**](struct.JsonConfig.html) allows to configure extraction +/// process. +/// +/// ## Example +/// +/// ```rust +/// #[macro_use] extern crate serde_derive; +/// use actix_web::{web, extract, App}; +/// +/// #[derive(Deserialize)] +/// struct Info { +/// username: String, +/// } +/// +/// /// deserialize `Info` from request's body +/// fn index(info: extract::Json) -> String { +/// format!("Welcome {}!", info.username) +/// } +/// +/// fn main() { +/// let app = App::new().resource( +/// "/index.html", +/// |r| r.route(web::post().to(index))); +/// } +/// ``` impl FromRequest

for Json where T: DeserializeOwned + 'static, @@ -632,7 +742,7 @@ impl Default for JsonConfig { /// /// Loads request's payload and construct Bytes instance. /// -/// [**PayloadConfig**](dev/struct.PayloadConfig.html) allows to configure +/// [**PayloadConfig**](struct.PayloadConfig.html) allows to configure /// extraction process. /// /// ## Example @@ -677,7 +787,7 @@ where /// /// Text extractor automatically decode body according to the request's charset. /// -/// [**PayloadConfig**](dev/struct.PayloadConfig.html) allows to configure +/// [**PayloadConfig**](struct.PayloadConfig.html) allows to configure /// extraction process. /// /// ## Example @@ -931,6 +1041,17 @@ impl Default for PayloadConfig { } } +#[doc(hidden)] +impl

FromRequest

for () { + type Error = Error; + type Future = FutureResult<(), Error>; + type Config = (); + + fn from_request(_req: &mut ServiceFromRequest

) -> Self::Future { + ok(()) + } +} + macro_rules! tuple_config ({ $($T:ident),+} => { impl<$($T,)+> ExtractorConfig for ($($T,)+) where $($T: ExtractorConfig + Clone,)+ @@ -944,6 +1065,7 @@ macro_rules! tuple_config ({ $($T:ident),+} => { macro_rules! tuple_from_req ({$fut_type:ident, $(($n:tt, $T:ident)),+} => { /// FromRequest implementation for tuple + #[doc(hidden)] impl + 'static),+> FromRequest

for ($($T,)+) { type Error = Error; @@ -995,16 +1117,6 @@ macro_rules! tuple_from_req ({$fut_type:ident, $(($n:tt, $T:ident)),+} => { } }); -impl

FromRequest

for () { - type Error = Error; - type Future = FutureResult<(), Error>; - type Config = (); - - fn from_request(_req: &mut ServiceFromRequest

) -> Self::Future { - ok(()) - } -} - #[rustfmt::skip] mod m { use super::*; diff --git a/src/lib.rs b/src/lib.rs index f61bf0ace..37fd75912 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ mod app; pub mod extract; -pub mod handler; +mod handler; // mod info; pub mod blocking; pub mod guard; @@ -19,7 +19,7 @@ pub mod test; pub use actix_http::Response as HttpResponse; pub use actix_http::{error, http, Error, HttpMessage, ResponseError, Result}; -pub use crate::app::App; +pub use crate::app::{App, AppRouter}; pub use crate::extract::{FromRequest, Json}; pub use crate::request::HttpRequest; pub use crate::resource::Resource; @@ -37,30 +37,37 @@ pub mod web { use crate::responder::Responder; use crate::Route; + /// Create **route** without configuration. pub fn route() -> Route

{ Route::new() } + /// Create **route** with `GET` method guard. pub fn get() -> Route

{ Route::get() } + /// Create **route** with `POST` method guard. pub fn post() -> Route

{ Route::post() } + /// Create **route** with `PUT` method guard. pub fn put() -> Route

{ Route::put() } + /// Create **route** with `DELETE` method guard. pub fn delete() -> Route

{ Route::delete() } + /// Create **route** with `HEAD` method guard. pub fn head() -> Route

{ Route::new().method(Method::HEAD) } + /// Create **route** and add method guard. pub fn method(method: Method) -> Route

{ Route::new().method(method) } diff --git a/src/request.rs b/src/request.rs index 48a2dd833..d90627f52 100644 --- a/src/request.rs +++ b/src/request.rs @@ -6,12 +6,12 @@ use std::rc::Rc; use actix_http::http::{HeaderMap, Method, Uri, Version}; use actix_http::{Error, Extensions, HttpMessage, Message, Payload, RequestHead}; use actix_router::{Path, Url}; -use futures::future::{ok, FutureResult}; use crate::extract::FromRequest; use crate::service::ServiceFromRequest; #[derive(Clone)] +/// An HTTP Request pub struct HttpRequest { pub(crate) head: Message, pub(crate) path: Path, @@ -20,7 +20,7 @@ pub struct HttpRequest { impl HttpRequest { #[inline] - pub fn new( + pub(crate) fn new( head: Message, path: Path, extensions: Rc, @@ -140,14 +140,33 @@ impl HttpMessage for HttpRequest { } } +/// It is possible to get `HttpRequest` as an extractor handler parameter +/// +/// ## Example +/// +/// ```rust +/// # #[macro_use] extern crate serde_derive; +/// use actix_web::{web, App, HttpRequest}; +/// +/// /// extract `Thing` from request +/// fn index(req: HttpRequest) -> String { +/// format!("Got thing: {:?}", req) +/// } +/// +/// fn main() { +/// let app = App::new().resource("/users/:first", |r| { +/// r.route(web::get().to(index)) +/// }); +/// } +/// ``` impl

FromRequest

for HttpRequest { type Error = Error; - type Future = FutureResult; + type Future = Result; type Config = (); #[inline] fn from_request(req: &mut ServiceFromRequest

) -> Self::Future { - ok(req.clone()) + Ok(req.clone()) } } diff --git a/src/resource.rs b/src/resource.rs index f05e998fa..342d801d4 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -18,10 +18,24 @@ use crate::service::{ServiceRequest, ServiceResponse}; type HttpService

= BoxedService, ServiceResponse, ()>; type HttpNewService

= BoxedNewService<(), ServiceRequest

, ServiceResponse, (), ()>; -/// Resource route definition +/// *Resource* is an entry in route table which corresponds to requested URL. /// -/// Route uses builder-like pattern for configuration. -/// If handler is not explicitly set, default *404 Not Found* handler is used. +/// Resource in turn has at least one route. +/// Route consists of an handlers objects and list of guards +/// (objects that implement `Guard` trait). +/// Resources and rouets uses builder-like pattern for configuration. +/// During request handling, resource object iterate through all routes +/// and check guards for specific route, if request matches all +/// guards, route considered matched and route handler get called. +/// +/// ```rust +/// use actix_web::{web, App, HttpResponse}; +/// +/// fn main() { +/// let app = App::new() +/// .resource( +/// "/", |r| r.route(web::get().to(|| HttpResponse::Ok()))); +/// } pub struct Resource> { routes: Vec>, endpoint: T, @@ -58,8 +72,6 @@ where >, { /// Register a new route. - /// *Route* is used for route configuration, i.e. adding guards, - /// setting up handler. /// /// ```rust /// use actix_web::{web, guard, App, HttpResponse}; @@ -74,12 +86,31 @@ where /// }); /// } /// ``` + /// + /// Multiple routes could be added to a resource. + /// + /// ```rust + /// use actix_web::{web, guard, App, HttpResponse}; + /// + /// fn main() { + /// let app = App::new() + /// .resource("/container/", |r| { + /// r.route(web::get().to(get_handler)) + /// .route(web::post().to(post_handler)) + /// .route(web::delete().to(delete_handler)) + /// }); + /// } + /// # fn get_handler() {} + /// # fn post_handler() {} + /// # fn delete_handler() {} + /// ``` pub fn route(mut self, route: Route

) -> Self { self.routes.push(route.finish()); self } - /// Register a new route and add handler. + /// Register a new route and add handler. This route get called for all + /// requests. /// /// ```rust /// use actix_web::*; @@ -148,7 +179,8 @@ where /// Register a resource middleware /// /// This is similar to `App's` middlewares, but - /// middlewares get invoked on resource level. + /// middleware is not allowed to change response type (i.e modify response's body). + /// Middleware get invoked on resource level. pub fn middleware( self, mw: F, diff --git a/src/state.rs b/src/state.rs index 168a8c899..d4e4c894e 100644 --- a/src/state.rs +++ b/src/state.rs @@ -3,7 +3,6 @@ use std::rc::Rc; use actix_http::error::{Error, ErrorInternalServerError}; use actix_http::Extensions; -use futures::future::{err, ok, FutureResult}; use futures::{Async, Future, IntoFuture, Poll}; use crate::extract::FromRequest; @@ -19,60 +18,61 @@ pub(crate) trait StateFactoryResult { } /// Application state -pub struct State(Rc); +pub struct State(Rc); -impl State { - pub fn new(state: S) -> State { +impl State { + pub(crate) fn new(state: T) -> State { State(Rc::new(state)) } - pub fn get_ref(&self) -> &S { + /// Get referecnce to inner state type. + pub fn get_ref(&self) -> &T { self.0.as_ref() } } -impl Deref for State { - type Target = S; +impl Deref for State { + type Target = T; - fn deref(&self) -> &S { + fn deref(&self) -> &T { self.0.as_ref() } } -impl Clone for State { - fn clone(&self) -> State { +impl Clone for State { + fn clone(&self) -> State { State(self.0.clone()) } } -impl FromRequest

for State { +impl FromRequest

for State { type Error = Error; - type Future = FutureResult; + type Future = Result; type Config = (); #[inline] fn from_request(req: &mut ServiceFromRequest

) -> Self::Future { - if let Some(st) = req.app_extensions().get::>() { - ok(st.clone()) + if let Some(st) = req.app_extensions().get::>() { + Ok(st.clone()) } else { - err(ErrorInternalServerError( - "State is not configured, use App::state()", + Err(ErrorInternalServerError( + "State is not configured, to configure use App::state()", )) } } } -impl StateFactory for State { +impl StateFactory for State { fn construct(&self) -> Box { Box::new(StateFut { st: self.clone() }) } } -struct StateFut { - st: State, +struct StateFut { + st: State, } -impl StateFactoryResult for StateFut { +impl StateFactoryResult for StateFut { fn poll_result(&mut self, extensions: &mut Extensions) -> Poll<(), ()> { extensions.insert(self.st.clone()); Ok(Async::Ready(())) @@ -92,17 +92,17 @@ where } } -struct StateFactoryFut +struct StateFactoryFut where - F: Future, + F: Future, F::Error: std::fmt::Debug, { fut: F, } -impl StateFactoryResult for StateFactoryFut +impl StateFactoryResult for StateFactoryFut where - F: Future, + F: Future, F::Error: std::fmt::Debug, { fn poll_result(&mut self, extensions: &mut Extensions) -> Poll<(), ()> {