1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-28 09:42:40 +01:00

update api docs

This commit is contained in:
Nikolay Kim 2019-03-03 14:45:56 -08:00
parent e50d4c5e0e
commit 360082f99f
6 changed files with 237 additions and 67 deletions

View File

@ -245,8 +245,8 @@ where
} }
} }
/// Structure that follows the builder pattern for building application /// Application router builder - Structure that follows the builder pattern
/// instances. /// for building application instances.
pub struct AppRouter<C, P, B, T> { pub struct AppRouter<C, P, B, T> {
chain: C, chain: C,
services: Vec<(ResourceDef, HttpNewService<P>)>, services: Vec<(ResourceDef, HttpNewService<P>)>,

View File

@ -165,17 +165,63 @@ impl<T> From<T> for Path<T> {
} }
} }
/// 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<Info>) -> Result<String, Error> {
/// 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<T, P> FromRequest<P> for Path<T> impl<T, P> FromRequest<P> for Path<T>
where where
T: DeserializeOwned, T: DeserializeOwned,
{ {
type Error = Error; type Error = Error;
type Future = FutureResult<Self, Error>; type Future = Result<Self, Error>;
type Config = (); type Config = ();
#[inline] #[inline]
fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future { fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future {
Self::extract(req).map_err(ErrorNotFound).into_future() Self::extract(req).map_err(ErrorNotFound)
} }
} }
@ -200,17 +246,17 @@ impl<T: fmt::Display> fmt::Display for Path<T> {
/// #[macro_use] extern crate serde_derive; /// #[macro_use] extern crate serde_derive;
/// use actix_web::{web, extract, App}; /// use actix_web::{web, extract, App};
/// ///
///#[derive(Debug, Deserialize)] /// #[derive(Debug, Deserialize)]
///pub enum ResponseType { /// pub enum ResponseType {
/// Token, /// Token,
/// Code /// Code
///} /// }
/// ///
///#[derive(Deserialize)] /// #[derive(Deserialize)]
///pub struct AuthRequest { /// pub struct AuthRequest {
/// id: u64, /// id: u64,
/// response_type: ResponseType, /// response_type: ResponseType,
///} /// }
/// ///
/// // Use `Query` extractor for query information. /// // Use `Query` extractor for query information.
/// // This handler get called only if request's query contains `username` field /// // This handler get called only if request's query contains `username` field
@ -248,19 +294,52 @@ impl<T> Query<T> {
} }
} }
/// 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<AuthRequest>) -> 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<T, P> FromRequest<P> for Query<T> impl<T, P> FromRequest<P> for Query<T>
where where
T: de::DeserializeOwned, T: de::DeserializeOwned,
{ {
type Error = Error; type Error = Error;
type Future = FutureResult<Self, Error>; type Future = Result<Self, Error>;
type Config = (); type Config = ();
#[inline] #[inline]
fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future { fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future {
serde_urlencoded::from_str::<T>(req.query_string()) serde_urlencoded::from_str::<T>(req.query_string())
.map(|val| ok(Query(val))) .map(|val| Ok(Query(val)))
.unwrap_or_else(|e| err(e.into())) .unwrap_or_else(|e| Err(e.into()))
} }
} }
@ -282,7 +361,7 @@ impl<T: fmt::Display> fmt::Display for Query<T> {
/// To extract typed information from request's body, the type `T` must /// To extract typed information from request's body, the type `T` must
/// implement the `Deserialize` trait from *serde*. /// implement the `Deserialize` trait from *serde*.
/// ///
/// [**FormConfig**](dev/struct.FormConfig.html) allows to configure extraction /// [**FormConfig**](struct.FormConfig.html) allows to configure extraction
/// process. /// process.
/// ///
/// ## Example /// ## Example
@ -436,7 +515,7 @@ impl Default for FormConfig {
/// To extract typed information from request's body, the type `T` must /// To extract typed information from request's body, the type `T` must
/// implement the `Deserialize` trait from *serde*. /// implement the `Deserialize` trait from *serde*.
/// ///
/// [**JsonConfig**](dev/struct.JsonConfig.html) allows to configure extraction /// [**JsonConfig**](struct.JsonConfig.html) allows to configure extraction
/// process. /// process.
/// ///
/// ## Example /// ## Example
@ -526,20 +605,51 @@ where
impl<T: Serialize> Responder for Json<T> { impl<T: Serialize> Responder for Json<T> {
type Error = Error; type Error = Error;
type Future = FutureResult<Response, Error>; type Future = Result<Response, Error>;
fn respond_to(self, _: &HttpRequest) -> Self::Future { fn respond_to(self, _: &HttpRequest) -> Self::Future {
let body = match serde_json::to_string(&self.0) { let body = match serde_json::to_string(&self.0) {
Ok(body) => body, 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") .content_type("application/json")
.body(body)) .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<Info>) -> String {
/// format!("Welcome {}!", info.username)
/// }
///
/// fn main() {
/// let app = App::new().resource(
/// "/index.html",
/// |r| r.route(web::post().to(index)));
/// }
/// ```
impl<T, P> FromRequest<P> for Json<T> impl<T, P> FromRequest<P> for Json<T>
where where
T: DeserializeOwned + 'static, T: DeserializeOwned + 'static,
@ -632,7 +742,7 @@ impl Default for JsonConfig {
/// ///
/// Loads request's payload and construct Bytes instance. /// 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. /// extraction process.
/// ///
/// ## Example /// ## Example
@ -677,7 +787,7 @@ where
/// ///
/// Text extractor automatically decode body according to the request's charset. /// 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. /// extraction process.
/// ///
/// ## Example /// ## Example
@ -931,6 +1041,17 @@ impl Default for PayloadConfig {
} }
} }
#[doc(hidden)]
impl<P> FromRequest<P> for () {
type Error = Error;
type Future = FutureResult<(), Error>;
type Config = ();
fn from_request(_req: &mut ServiceFromRequest<P>) -> Self::Future {
ok(())
}
}
macro_rules! tuple_config ({ $($T:ident),+} => { macro_rules! tuple_config ({ $($T:ident),+} => {
impl<$($T,)+> ExtractorConfig for ($($T,)+) impl<$($T,)+> ExtractorConfig for ($($T,)+)
where $($T: ExtractorConfig + Clone,)+ 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)),+} => { macro_rules! tuple_from_req ({$fut_type:ident, $(($n:tt, $T:ident)),+} => {
/// FromRequest implementation for tuple /// FromRequest implementation for tuple
#[doc(hidden)]
impl<P, $($T: FromRequest<P> + 'static),+> FromRequest<P> for ($($T,)+) impl<P, $($T: FromRequest<P> + 'static),+> FromRequest<P> for ($($T,)+)
{ {
type Error = Error; type Error = Error;
@ -995,16 +1117,6 @@ macro_rules! tuple_from_req ({$fut_type:ident, $(($n:tt, $T:ident)),+} => {
} }
}); });
impl<P> FromRequest<P> for () {
type Error = Error;
type Future = FutureResult<(), Error>;
type Config = ();
fn from_request(_req: &mut ServiceFromRequest<P>) -> Self::Future {
ok(())
}
}
#[rustfmt::skip] #[rustfmt::skip]
mod m { mod m {
use super::*; use super::*;

View File

@ -2,7 +2,7 @@
mod app; mod app;
pub mod extract; pub mod extract;
pub mod handler; mod handler;
// mod info; // mod info;
pub mod blocking; pub mod blocking;
pub mod guard; pub mod guard;
@ -19,7 +19,7 @@ pub mod test;
pub use actix_http::Response as HttpResponse; pub use actix_http::Response as HttpResponse;
pub use actix_http::{error, http, Error, HttpMessage, ResponseError, Result}; 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::extract::{FromRequest, Json};
pub use crate::request::HttpRequest; pub use crate::request::HttpRequest;
pub use crate::resource::Resource; pub use crate::resource::Resource;
@ -37,30 +37,37 @@ pub mod web {
use crate::responder::Responder; use crate::responder::Responder;
use crate::Route; use crate::Route;
/// Create **route** without configuration.
pub fn route<P: 'static>() -> Route<P> { pub fn route<P: 'static>() -> Route<P> {
Route::new() Route::new()
} }
/// Create **route** with `GET` method guard.
pub fn get<P: 'static>() -> Route<P> { pub fn get<P: 'static>() -> Route<P> {
Route::get() Route::get()
} }
/// Create **route** with `POST` method guard.
pub fn post<P: 'static>() -> Route<P> { pub fn post<P: 'static>() -> Route<P> {
Route::post() Route::post()
} }
/// Create **route** with `PUT` method guard.
pub fn put<P: 'static>() -> Route<P> { pub fn put<P: 'static>() -> Route<P> {
Route::put() Route::put()
} }
/// Create **route** with `DELETE` method guard.
pub fn delete<P: 'static>() -> Route<P> { pub fn delete<P: 'static>() -> Route<P> {
Route::delete() Route::delete()
} }
/// Create **route** with `HEAD` method guard.
pub fn head<P: 'static>() -> Route<P> { pub fn head<P: 'static>() -> Route<P> {
Route::new().method(Method::HEAD) Route::new().method(Method::HEAD)
} }
/// Create **route** and add method guard.
pub fn method<P: 'static>(method: Method) -> Route<P> { pub fn method<P: 'static>(method: Method) -> Route<P> {
Route::new().method(method) Route::new().method(method)
} }

View File

@ -6,12 +6,12 @@ use std::rc::Rc;
use actix_http::http::{HeaderMap, Method, Uri, Version}; use actix_http::http::{HeaderMap, Method, Uri, Version};
use actix_http::{Error, Extensions, HttpMessage, Message, Payload, RequestHead}; use actix_http::{Error, Extensions, HttpMessage, Message, Payload, RequestHead};
use actix_router::{Path, Url}; use actix_router::{Path, Url};
use futures::future::{ok, FutureResult};
use crate::extract::FromRequest; use crate::extract::FromRequest;
use crate::service::ServiceFromRequest; use crate::service::ServiceFromRequest;
#[derive(Clone)] #[derive(Clone)]
/// An HTTP Request
pub struct HttpRequest { pub struct HttpRequest {
pub(crate) head: Message<RequestHead>, pub(crate) head: Message<RequestHead>,
pub(crate) path: Path<Url>, pub(crate) path: Path<Url>,
@ -20,7 +20,7 @@ pub struct HttpRequest {
impl HttpRequest { impl HttpRequest {
#[inline] #[inline]
pub fn new( pub(crate) fn new(
head: Message<RequestHead>, head: Message<RequestHead>,
path: Path<Url>, path: Path<Url>,
extensions: Rc<Extensions>, extensions: Rc<Extensions>,
@ -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<P> FromRequest<P> for HttpRequest { impl<P> FromRequest<P> for HttpRequest {
type Error = Error; type Error = Error;
type Future = FutureResult<Self, Error>; type Future = Result<Self, Error>;
type Config = (); type Config = ();
#[inline] #[inline]
fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future { fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future {
ok(req.clone()) Ok(req.clone())
} }
} }

View File

@ -18,10 +18,24 @@ use crate::service::{ServiceRequest, ServiceResponse};
type HttpService<P> = BoxedService<ServiceRequest<P>, ServiceResponse, ()>; type HttpService<P> = BoxedService<ServiceRequest<P>, ServiceResponse, ()>;
type HttpNewService<P> = BoxedNewService<(), ServiceRequest<P>, ServiceResponse, (), ()>; type HttpNewService<P> = BoxedNewService<(), ServiceRequest<P>, ServiceResponse, (), ()>;
/// Resource route definition /// *Resource* is an entry in route table which corresponds to requested URL.
/// ///
/// Route uses builder-like pattern for configuration. /// Resource in turn has at least one route.
/// If handler is not explicitly set, default *404 Not Found* handler is used. /// 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<P, T = ResourceEndpoint<P>> { pub struct Resource<P, T = ResourceEndpoint<P>> {
routes: Vec<Route<P>>, routes: Vec<Route<P>>,
endpoint: T, endpoint: T,
@ -58,8 +72,6 @@ where
>, >,
{ {
/// Register a new route. /// Register a new route.
/// *Route* is used for route configuration, i.e. adding guards,
/// setting up handler.
/// ///
/// ```rust /// ```rust
/// use actix_web::{web, guard, App, HttpResponse}; /// 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<P>) -> Self { pub fn route(mut self, route: Route<P>) -> Self {
self.routes.push(route.finish()); self.routes.push(route.finish());
self self
} }
/// Register a new route and add handler. /// Register a new route and add handler. This route get called for all
/// requests.
/// ///
/// ```rust /// ```rust
/// use actix_web::*; /// use actix_web::*;
@ -148,7 +179,8 @@ where
/// Register a resource middleware /// Register a resource middleware
/// ///
/// This is similar to `App's` middlewares, but /// 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<M, F>( pub fn middleware<M, F>(
self, self,
mw: F, mw: F,

View File

@ -3,7 +3,6 @@ use std::rc::Rc;
use actix_http::error::{Error, ErrorInternalServerError}; use actix_http::error::{Error, ErrorInternalServerError};
use actix_http::Extensions; use actix_http::Extensions;
use futures::future::{err, ok, FutureResult};
use futures::{Async, Future, IntoFuture, Poll}; use futures::{Async, Future, IntoFuture, Poll};
use crate::extract::FromRequest; use crate::extract::FromRequest;
@ -19,60 +18,61 @@ pub(crate) trait StateFactoryResult {
} }
/// Application state /// Application state
pub struct State<S>(Rc<S>); pub struct State<T>(Rc<T>);
impl<S> State<S> { impl<T> State<T> {
pub fn new(state: S) -> State<S> { pub(crate) fn new(state: T) -> State<T> {
State(Rc::new(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() self.0.as_ref()
} }
} }
impl<S> Deref for State<S> { impl<T> Deref for State<T> {
type Target = S; type Target = T;
fn deref(&self) -> &S { fn deref(&self) -> &T {
self.0.as_ref() self.0.as_ref()
} }
} }
impl<S> Clone for State<S> { impl<T> Clone for State<T> {
fn clone(&self) -> State<S> { fn clone(&self) -> State<T> {
State(self.0.clone()) State(self.0.clone())
} }
} }
impl<S: 'static, P> FromRequest<P> for State<S> { impl<T: 'static, P> FromRequest<P> for State<T> {
type Error = Error; type Error = Error;
type Future = FutureResult<Self, Error>; type Future = Result<Self, Error>;
type Config = (); type Config = ();
#[inline] #[inline]
fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future { fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future {
if let Some(st) = req.app_extensions().get::<State<S>>() { if let Some(st) = req.app_extensions().get::<State<T>>() {
ok(st.clone()) Ok(st.clone())
} else { } else {
err(ErrorInternalServerError( Err(ErrorInternalServerError(
"State is not configured, use App::state()", "State is not configured, to configure use App::state()",
)) ))
} }
} }
} }
impl<S: 'static> StateFactory for State<S> { impl<T: 'static> StateFactory for State<T> {
fn construct(&self) -> Box<StateFactoryResult> { fn construct(&self) -> Box<StateFactoryResult> {
Box::new(StateFut { st: self.clone() }) Box::new(StateFut { st: self.clone() })
} }
} }
struct StateFut<S> { struct StateFut<T> {
st: State<S>, st: State<T>,
} }
impl<S: 'static> StateFactoryResult for StateFut<S> { impl<T: 'static> StateFactoryResult for StateFut<T> {
fn poll_result(&mut self, extensions: &mut Extensions) -> Poll<(), ()> { fn poll_result(&mut self, extensions: &mut Extensions) -> Poll<(), ()> {
extensions.insert(self.st.clone()); extensions.insert(self.st.clone());
Ok(Async::Ready(())) Ok(Async::Ready(()))
@ -92,17 +92,17 @@ where
} }
} }
struct StateFactoryFut<S, F> struct StateFactoryFut<T, F>
where where
F: Future<Item = S>, F: Future<Item = T>,
F::Error: std::fmt::Debug, F::Error: std::fmt::Debug,
{ {
fut: F, fut: F,
} }
impl<S: 'static, F> StateFactoryResult for StateFactoryFut<S, F> impl<T: 'static, F> StateFactoryResult for StateFactoryFut<T, F>
where where
F: Future<Item = S>, F: Future<Item = T>,
F::Error: std::fmt::Debug, F::Error: std::fmt::Debug,
{ {
fn poll_result(&mut self, extensions: &mut Extensions) -> Poll<(), ()> { fn poll_result(&mut self, extensions: &mut Extensions) -> Poll<(), ()> {