1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-06-11 09:49:29 +00:00

added State extractor

This commit is contained in:
Nikolay Kim 2018-03-29 15:41:13 -07:00
parent d24752d9bc
commit 3e98177fad
4 changed files with 90 additions and 76 deletions

View file

@ -33,7 +33,7 @@ Actix web is a simple, pragmatic, extremely fast, web framework for Rust.
```rust
extern crate actix_web;
use actix_web::*;
use actix_web::{Application, HttpServer, Path};
fn index(info: Path<(String, u32)>) -> String {
format!("Hello {}! id:{}", info.0, info.1)

105
src/de.rs
View file

@ -12,8 +12,6 @@ use httprequest::HttpRequest;
/// Extract typed information from the request's path.
///
/// `S` - application state type
///
/// ## Example
///
/// ```rust
@ -49,66 +47,48 @@ use httprequest::HttpRequest;
/// # use actix_web::*;
/// use actix_web::Path;
///
/// /// Application state
/// struct State {}
///
/// #[derive(Deserialize)]
/// struct Info {
/// username: String,
/// }
///
/// /// extract path info using serde
/// fn index(info: Path<Info, State>) -> Result<String> {
/// fn index(info: Path<Info>) -> Result<String> {
/// Ok(format!("Welcome {}!", info.username))
/// }
///
/// fn main() {
/// let app = Application::with_state(State{}).resource(
/// let app = Application::new().resource(
/// "/{username}/index.html", // <- define path parameters
/// |r| r.method(Method::GET).with(index)); // <- use `with` extractor
/// }
/// ```
pub struct Path<T, S=()>{
item: T,
req: HttpRequest<S>,
pub struct Path<T>{
inner: T
}
impl<T, S> Deref for Path<T, S> {
impl<T> Deref for Path<T> {
type Target = T;
fn deref(&self) -> &T {
&self.item
&self.inner
}
}
impl<T, S> DerefMut for Path<T, S> {
impl<T> DerefMut for Path<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.item
&mut self.inner
}
}
impl<T, S> Path<T, S> {
/// Shared application state
#[inline]
pub fn state(&self) -> &S {
self.req.state()
impl<T> Path<T> {
/// Deconstruct to a inner value
pub fn into_inner(self) -> T {
self.inner
}
/// Incoming request
#[inline]
pub fn request(&self) -> &HttpRequest<S> {
&self.req
}
/// Deconstruct instance into parts
pub fn into(self) -> (T, HttpRequest<S>) {
(self.item, self.req)
}
}
impl<T, S> FromRequest<S> for Path<T, S>
impl<T, S> FromRequest<S> for Path<T>
where T: DeserializeOwned, S: 'static
{
type Result = FutureResult<Self, Error>;
@ -118,14 +98,12 @@ impl<T, S> FromRequest<S> for Path<T, S>
let req = req.clone();
result(de::Deserialize::deserialize(PathDeserializer{req: &req})
.map_err(|e| e.into())
.map(|item| Path{item, req}))
.map(|inner| Path{inner}))
}
}
/// Extract typed information from from the request's query.
///
/// `S` - application state type
///
/// ## Example
///
/// ```rust
@ -136,9 +114,6 @@ impl<T, S> FromRequest<S> for Path<T, S>
/// # use actix_web::*;
/// use actix_web::Query;
///
/// /// Application state
/// struct State {}
///
/// #[derive(Deserialize)]
/// struct Info {
/// username: String,
@ -146,56 +121,40 @@ impl<T, S> FromRequest<S> for Path<T, S>
///
/// // use `with` extractor for query info
/// // this handler get called only if request's query contains `username` field
/// fn index(info: Query<Info, State>) -> Result<String> {
/// fn index(info: Query<Info>) -> Result<String> {
/// Ok(format!("Welcome {}!", info.username))
/// }
///
/// fn main() {
/// let app = Application::with_state(State{}).resource(
/// let app = Application::new().resource(
/// "/index.html",
/// |r| r.method(Method::GET).with(index)); // <- use `with` extractor
/// }
/// ```
pub struct Query<T, S=()>{
item: T,
req: HttpRequest<S>,
}
pub struct Query<T>(T);
impl<T, S> Deref for Query<T, S> {
impl<T> Deref for Query<T> {
type Target = T;
fn deref(&self) -> &T {
&self.item
&self.0
}
}
impl<T, S> DerefMut for Query<T, S> {
impl<T> DerefMut for Query<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.item
&mut self.0
}
}
impl<T, S> Query<T, S> {
/// Shared application state
#[inline]
pub fn state(&self) -> &S {
self.req.state()
}
/// Incoming request
#[inline]
pub fn request(&self) -> &HttpRequest<S> {
&self.req
}
/// Deconstruct instance into parts
pub fn into(self) -> (T, HttpRequest<S>) {
(self.item, self.req)
impl<T> Query<T> {
/// Deconstruct to a inner value
pub fn into_inner(self) -> T {
self.0
}
}
impl<T, S> FromRequest<S> for Query<T, S>
impl<T, S> FromRequest<S> for Query<T>
where T: de::DeserializeOwned, S: 'static
{
type Result = FutureResult<Self, Error>;
@ -205,7 +164,7 @@ impl<T, S> FromRequest<S> for Query<T, S>
let req = req.clone();
result(serde_urlencoded::from_str::<T>(req.query_string())
.map_err(|e| e.into())
.map(|item| Query{ item, req}))
.map(Query))
}
}
@ -581,7 +540,7 @@ mod tests {
let (router, _) = Router::new("", ServerSettings::default(), routes);
assert!(router.recognize(&mut req).is_some());
match Path::<MyStruct, _>::from_request(&req).poll().unwrap() {
match Path::<MyStruct>::from_request(&req).poll().unwrap() {
Async::Ready(s) => {
assert_eq!(s.key, "name");
assert_eq!(s.value, "user1");
@ -589,7 +548,7 @@ mod tests {
_ => unreachable!(),
}
match Path::<(String, String), _>::from_request(&req).poll().unwrap() {
match Path::<(String, String)>::from_request(&req).poll().unwrap() {
Async::Ready(s) => {
assert_eq!(s.0, "name");
assert_eq!(s.1, "user1");
@ -597,7 +556,7 @@ mod tests {
_ => unreachable!(),
}
match Query::<Id, _>::from_request(&req).poll().unwrap() {
match Query::<Id>::from_request(&req).poll().unwrap() {
Async::Ready(s) => {
assert_eq!(s.id, "test");
},
@ -607,7 +566,7 @@ mod tests {
let mut req = TestRequest::with_uri("/name/32/").finish();
assert!(router.recognize(&mut req).is_some());
match Path::<Test2, _>::from_request(&req).poll().unwrap() {
match Path::<Test2>::from_request(&req).poll().unwrap() {
Async::Ready(s) => {
assert_eq!(s.key, "name");
assert_eq!(s.value, 32);
@ -615,7 +574,7 @@ mod tests {
_ => unreachable!(),
}
match Path::<(String, u8), _>::from_request(&req).poll().unwrap() {
match Path::<(String, u8)>::from_request(&req).poll().unwrap() {
Async::Ready(s) => {
assert_eq!(s.0, "name");
assert_eq!(s.1, 32);

View file

@ -1,5 +1,6 @@
use std::ops::Deref;
use std::marker::PhantomData;
use futures::future::{Future, ok, err};
use futures::future::{Future, FutureResult, ok, err};
use error::Error;
use httprequest::HttpRequest;
@ -347,3 +348,57 @@ impl<S, H, F, R, E> RouteHandler<S> for AsyncHandler<S, H, F, R, E>
Reply::async(fut)
}
}
/// Access to an application state
///
/// `S` - application state type
///
/// ## Example
///
/// ```rust
/// # extern crate bytes;
/// # extern crate actix_web;
/// # extern crate futures;
/// # use actix_web::*;
/// #[macro_use] extern crate serde_derive;
/// use actix_web::State;
///
/// /// Application state
/// struct App {msg: &'static str}
///
/// #[derive(Deserialize)]
/// struct Info {
/// username: String,
/// }
///
/// /// extract path info using serde
/// fn index(state: State<App>, info: Path<Info>) -> Result<String> {
/// Ok(format!("{} {}!", state.msg, info.username))
/// }
///
/// fn main() {
/// let app = Application::with_state(App{msg: "Welcome"}).resource(
/// "/{username}/index.html", // <- define path parameters
/// |r| r.method(Method::GET).with2(index)); // <- use `with` extractor
/// }
/// ```
pub struct State<S> (HttpRequest<S>);
impl<S> Deref for State<S> {
type Target = S;
fn deref(&self) -> &S {
self.0.state()
}
}
impl<S: 'static> FromRequest<S> for State<S>
{
type Result = FutureResult<Self, Error>;
#[inline]
fn from_request(req: &HttpRequest<S>) -> Self::Result {
ok(State(req.clone()))
}
}

View file

@ -141,7 +141,7 @@ pub use application::Application;
pub use httpmessage::HttpMessage;
pub use httprequest::HttpRequest;
pub use httpresponse::HttpResponse;
pub use handler::{Either, Responder, AsyncResponder, FutureResponse};
pub use handler::{Either, Responder, AsyncResponder, FutureResponse, State};
pub use context::HttpContext;
pub use server::HttpServer;