diff --git a/README.md b/README.md index 5bdb88dd1..d128ff999 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ fn main() { Application::default("/") .middleware(middlewares::Logger::default()) // <- register logger middleware .resource("/ws/", |r| r.get(|req| ws::start(req, MyWebSocket))) // <- websocket route - .route("/", StaticFiles::new("examples/static/", true))) // <- server static files + .route("/", fs::StaticFiles::new("examples/static/", true))) // <- serve static files .serve::<_, ()>("127.0.0.1:8080").unwrap(); Arbiter::system().send(msgs::SystemExit(0)); diff --git a/build.rs b/build.rs index dae9e0626..f6ff9cb29 100644 --- a/build.rs +++ b/build.rs @@ -12,8 +12,18 @@ fn main() { // generates doc tests for `README.md`. skeptic::generate_doc_tests( &["README.md", + "guide/src/qs_1.md", "guide/src/qs_2.md", "guide/src/qs_3.md", + "guide/src/qs_4.md", + "guide/src/qs_5.md", + "guide/src/qs_6.md", + "guide/src/qs_7.md", + "guide/src/qs_9.md", + "guide/src/qs_10.md", + "guide/src/qs_11.md", + "guide/src/qs_12.md", + "guide/src/qs_13.md", ]); } else { let _ = fs::File::create(f); diff --git a/examples/basic.rs b/examples/basic.rs index fc2a55355..f42642f0e 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -89,7 +89,7 @@ fn main() { } }) // static files - .route("/static", StaticFiles::new("examples/static/", true))) + .route("/static", fs::StaticFiles::new("examples/static/", true))) .serve::<_, ()>("127.0.0.1:8080").unwrap(); println!("Started http server: 127.0.0.1:8080"); diff --git a/examples/websocket-chat/src/main.rs b/examples/websocket-chat/src/main.rs index ae123d5ee..8a83e35dc 100644 --- a/examples/websocket-chat/src/main.rs +++ b/examples/websocket-chat/src/main.rs @@ -208,7 +208,7 @@ fn main() { // websocket .resource("/ws/", |r| r.get(chat_route)) // static resources - .route("/static", StaticFiles::new("static/", true))) + .route("/static", fs::StaticFiles::new("static/", true))) .serve::<_, ()>("127.0.0.1:8080").unwrap(); let _ = sys.run(); diff --git a/examples/websocket.rs b/examples/websocket.rs index 24a88d03b..655fca05e 100644 --- a/examples/websocket.rs +++ b/examples/websocket.rs @@ -67,7 +67,7 @@ fn main() { // websocket route .resource("/ws/", |r| r.get(ws_index)) // static files - .route("/", StaticFiles::new("examples/static/", true))) + .route("/", fs::StaticFiles::new("examples/static/", true))) // start http server on 127.0.0.1:8080 .serve::<_, ()>("127.0.0.1:8080").unwrap(); diff --git a/guide/src/qs_4.md b/guide/src/qs_4.md index 969d01b8f..1675b3a19 100644 --- a/guide/src/qs_4.md +++ b/guide/src/qs_4.md @@ -6,14 +6,14 @@ A request handler can by any object that implements By default actix provdes several `Handler` implementations: * Simple function that accepts `HttpRequest` and returns any object that - can be converted to `HttpResponse` + implements `FromRequest` trait * Function that accepts `HttpRequest` and returns `Result>` object. * Function that accepts `HttpRequest` and return actor that has `HttpContext`as a context. -Actix provides response conversion into `HttpResponse` for some standard types, +Actix provides response `FromRequest` implementation for some standard types, like `&'static str`, `String`, etc. For complete list of implementations check -[HttpResponse documentation](../actix_web/struct.HttpResponse.html#implementations). +[FromRequest documentation](../actix_web/trait.FromRequest.html#foreign-impls). Examples: @@ -58,19 +58,18 @@ struct MyObj { name: String, } -/// we have to convert Error into HttpResponse as well, but with -/// specialization this could be handled genericly. -impl Into for MyObj { - fn into(self) -> HttpResponse { - let body = match serde_json::to_string(&self) { - Err(err) => return Error::from(err).into(), - Ok(body) => body, - }; +/// we have to convert Error into HttpResponse as well +impl FromRequest for MyObj { + type Item = HttpResponse; + type Error = Error; + + fn from_request(self, req: HttpRequest) -> Result { + let body = serde_json::to_string(&self)?; // Create response and set content type - HttpResponse::Ok() + Ok(HttpResponse::Ok() .content_type("application/json") - .body(body).unwrap() + .body(body)?) } } @@ -90,20 +89,6 @@ fn main() { } ``` -If `specialization` is enabled, conversion could be simplier: - -```rust,ignore -impl Into> for MyObj { - fn into(self) -> Result { - let body = serde_json::to_string(&self)?; - - Ok(HttpResponse::Ok() - .content_type("application/json") - .body(body)?) - } -} -``` - ## Async handlers There are two different types of async handlers. diff --git a/guide/src/qs_5.md b/guide/src/qs_5.md index adf96c903..849056dc1 100644 --- a/guide/src/qs_5.md +++ b/guide/src/qs_5.md @@ -65,11 +65,11 @@ used later in a request handler to access the matched value for that part. This done by looking up the identifier in the `HttpRequest.match_info` object: ```rust -extern crate actix; +extern crate actix_web; use actix_web::*; -fn index(req: Httprequest) -> String { - format!("Hello, {}", req.match_info["name"]) +fn index(req: HttpRequest) -> String { + format!("Hello, {}", &req.match_info()["name"]) } fn main() { @@ -96,13 +96,13 @@ implements `FromParam` trait. For example most of standard integer types implements `FromParam` trait. i.e.: ```rust -extern crate actix; +extern crate actix_web; use actix_web::*; -fn index(req: Httprequest) -> String { +fn index(req: HttpRequest) -> Result { let v1: u8 = req.match_info().query("v1")?; let v2: u8 = req.match_info().query("v2")?; - format!("Values {} {}", v1, v2) + Ok(format!("Values {} {}", v1, v2)) } fn main() { @@ -146,18 +146,18 @@ safe to interpolate within, or use as a suffix of, a path without additional checks. ```rust -extern crate actix; +extern crate actix_web; use actix_web::*; use std::path::PathBuf; -fn index(req: Httprequest) -> String { +fn index(req: HttpRequest) -> Result { let path: PathBuf = req.match_info().query("tail")?; - format!("Path {:?}", path) + Ok(format!("Path {:?}", path)) } fn main() { Application::default("/") - .resource(r"/a/{tail:**}", |r| r.get(index)) + .resource(r"/a/{tail:*}", |r| r.get(index)) .finish(); } ``` diff --git a/src/dev.rs b/src/dev.rs index 537590651..7e597c9d6 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -9,8 +9,8 @@ //! ``` // dev specific +pub use route::Handler; pub use pipeline::Pipeline; -pub use route::{Handler, FromRequest}; pub use channel::{HttpChannel, HttpHandler}; pub use recognizer::{FromParam, RouteRecognizer}; diff --git a/src/error.rs b/src/error.rs index 606a92580..8ed5ccf97 100644 --- a/src/error.rs +++ b/src/error.rs @@ -31,7 +31,7 @@ use httpcodes::{HTTPBadRequest, HTTPMethodNotAllowed, HTTPExpectationFailed}; pub type Result = result::Result; /// General purpose actix web error -#[derive(Debug)] +#[derive(Fail, Debug)] pub struct Error { cause: Box, } diff --git a/src/fs.rs b/src/fs.rs index 0618fa40c..75650a05e 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -1,6 +1,6 @@ //! Static files support. -//! -//! TODO: needs to re-implement actual files handling, current impl blocks + +// //! TODO: needs to re-implement actual files handling, current impl blocks use std::io; use std::io::Read; use std::fmt::Write; @@ -19,11 +19,10 @@ use httpcodes::{HTTPOk, HTTPNotFound}; /// /// ```rust /// extern crate actix_web; -/// use actix_web::*; /// /// fn main() { -/// let app = Application::default("/") -/// .route("/static", StaticFiles::new(".", true)) +/// let app = actix_web::Application::default("/") +/// .route("/static", actix_web::fs::StaticFiles::new(".", true)) /// .finish(); /// } /// ``` @@ -39,6 +38,7 @@ impl StaticFiles { /// Create new `StaticFiles` instance /// /// `dir` - base directory + /// /// `index` - show index for directory pub fn new>(dir: D, index: bool) -> StaticFiles { let dir = dir.into(); diff --git a/src/httpcodes.rs b/src/httpcodes.rs index 013e16d39..64d308d5f 100644 --- a/src/httpcodes.rs +++ b/src/httpcodes.rs @@ -1,6 +1,6 @@ //! Basic http responses #![allow(non_upper_case_globals)] -use http::StatusCode; +use http::{StatusCode, Error as HttpError}; use body::Body; use route::{Reply, RouteHandler, FromRequest}; @@ -74,8 +74,11 @@ impl RouteHandler for StaticResponse { } impl FromRequest for StaticResponse { - fn from_request(self, _: HttpRequest) -> Reply { - Reply::response(HttpResponse::new(self.0, Body::Empty)) + type Item = HttpResponse; + type Error = HttpError; + + fn from_request(self, _: HttpRequest) -> Result { + self.build().body(Body::Empty) } } diff --git a/src/httpresponse.rs b/src/httpresponse.rs index 35499ef55..21663853c 100644 --- a/src/httpresponse.rs +++ b/src/httpresponse.rs @@ -8,11 +8,10 @@ use http::{StatusCode, Version, HeaderMap, HttpTryFrom, Error as HttpError}; use http::header::{self, HeaderName, HeaderValue}; use serde_json; use serde::Serialize; - use Cookie; use body::Body; use error::Error; -use route::{Reply, FromRequest}; +use route::FromRequest; use encoding::ContentEncoding; use httprequest::HttpRequest; @@ -461,8 +460,11 @@ impl From for HttpResponse { } impl FromRequest for HttpResponseBuilder { - fn from_request(self, _: HttpRequest) -> Reply { - Reply::response(self) + type Item = HttpResponse; + type Error = HttpError; + + fn from_request(mut self, _: HttpRequest) -> Result { + self.finish() } } @@ -476,11 +478,13 @@ impl From<&'static str> for HttpResponse { } impl FromRequest for &'static str { - fn from_request(self, req: HttpRequest) -> Reply { + type Item = HttpResponse; + type Error = HttpError; + + fn from_request(self, _: HttpRequest) -> Result { HttpResponse::build(StatusCode::OK) .content_type("text/plain; charset=utf-8") .body(self) - .from_request(req) } } @@ -494,11 +498,13 @@ impl From<&'static [u8]> for HttpResponse { } impl FromRequest for &'static [u8] { - fn from_request(self, req: HttpRequest) -> Reply { + type Item = HttpResponse; + type Error = HttpError; + + fn from_request(self, _: HttpRequest) -> Result { HttpResponse::build(StatusCode::OK) .content_type("application/octet-stream") .body(self) - .from_request(req) } } @@ -512,11 +518,13 @@ impl From for HttpResponse { } impl FromRequest for String { - fn from_request(self, req: HttpRequest) -> Reply { + type Item = HttpResponse; + type Error = HttpError; + + fn from_request(self, _: HttpRequest) -> Result { HttpResponse::build(StatusCode::OK) .content_type("text/plain; charset=utf-8") .body(self) - .from_request(req) } } @@ -530,11 +538,13 @@ impl<'a> From<&'a String> for HttpResponse { } impl<'a> FromRequest for &'a String { - fn from_request(self, req: HttpRequest) -> Reply { + type Item = HttpResponse; + type Error = HttpError; + + fn from_request(self, _: HttpRequest) -> Result { HttpResponse::build(StatusCode::OK) .content_type("text/plain; charset=utf-8") .body(self) - .from_request(req) } } @@ -548,11 +558,13 @@ impl From for HttpResponse { } impl FromRequest for Bytes { - fn from_request(self, req: HttpRequest) -> Reply { + type Item = HttpResponse; + type Error = HttpError; + + fn from_request(self, _: HttpRequest) -> Result { HttpResponse::build(StatusCode::OK) .content_type("application/octet-stream") .body(self) - .from_request(req) } } @@ -566,11 +578,13 @@ impl From for HttpResponse { } impl FromRequest for BytesMut { - fn from_request(self, req: HttpRequest) -> Reply { + type Item = HttpResponse; + type Error = HttpError; + + fn from_request(self, _: HttpRequest) -> Result { HttpResponse::build(StatusCode::OK) .content_type("application/octet-stream") .body(self) - .from_request(req) } } @@ -650,6 +664,8 @@ mod tests { #[test] fn test_into_response() { + let req = HttpRequest::default(); + let resp: HttpResponse = "test".into(); assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.headers().get(header::CONTENT_TYPE).unwrap(), @@ -657,6 +673,13 @@ mod tests { assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.body().binary().unwrap(), &Binary::from("test")); + let resp: HttpResponse = "test".from_request(req.clone()).ok().unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.headers().get(header::CONTENT_TYPE).unwrap(), + header::HeaderValue::from_static("text/plain; charset=utf-8")); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.body().binary().unwrap(), &Binary::from("test")); + let resp: HttpResponse = b"test".as_ref().into(); assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.headers().get(header::CONTENT_TYPE).unwrap(), @@ -664,6 +687,13 @@ mod tests { assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.body().binary().unwrap(), &Binary::from(b"test".as_ref())); + let resp: HttpResponse = b"test".as_ref().from_request(req.clone()).ok().unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.headers().get(header::CONTENT_TYPE).unwrap(), + header::HeaderValue::from_static("application/octet-stream")); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.body().binary().unwrap(), &Binary::from(b"test".as_ref())); + let resp: HttpResponse = "test".to_owned().into(); assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.headers().get(header::CONTENT_TYPE).unwrap(), @@ -671,6 +701,13 @@ mod tests { assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.body().binary().unwrap(), &Binary::from("test".to_owned())); + let resp: HttpResponse = "test".to_owned().from_request(req.clone()).ok().unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.headers().get(header::CONTENT_TYPE).unwrap(), + header::HeaderValue::from_static("text/plain; charset=utf-8")); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.body().binary().unwrap(), &Binary::from("test".to_owned())); + let resp: HttpResponse = (&"test".to_owned()).into(); assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.headers().get(header::CONTENT_TYPE).unwrap(), @@ -678,6 +715,13 @@ mod tests { assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.body().binary().unwrap(), &Binary::from((&"test".to_owned()))); + let resp: HttpResponse = (&"test".to_owned()).from_request(req.clone()).ok().unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.headers().get(header::CONTENT_TYPE).unwrap(), + header::HeaderValue::from_static("text/plain; charset=utf-8")); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.body().binary().unwrap(), &Binary::from((&"test".to_owned()))); + let b = Bytes::from_static(b"test"); let resp: HttpResponse = b.into(); assert_eq!(resp.status(), StatusCode::OK); @@ -686,6 +730,14 @@ mod tests { assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.body().binary().unwrap(), &Binary::from(Bytes::from_static(b"test"))); + let b = Bytes::from_static(b"test"); + let resp: HttpResponse = b.from_request(req.clone()).ok().unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.headers().get(header::CONTENT_TYPE).unwrap(), + header::HeaderValue::from_static("application/octet-stream")); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.body().binary().unwrap(), &Binary::from(Bytes::from_static(b"test"))); + let b = BytesMut::from("test"); let resp: HttpResponse = b.into(); assert_eq!(resp.status(), StatusCode::OK); @@ -693,5 +745,13 @@ mod tests { header::HeaderValue::from_static("application/octet-stream")); assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.body().binary().unwrap(), &Binary::from(BytesMut::from("test"))); + + let b = BytesMut::from("test"); + let resp: HttpResponse = b.from_request(req.clone()).ok().unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.headers().get(header::CONTENT_TYPE).unwrap(), + header::HeaderValue::from_static("application/octet-stream")); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.body().binary().unwrap(), &Binary::from(BytesMut::from("test"))); } } diff --git a/src/lib.rs b/src/lib.rs index ddcf67e85..61c6a0a4f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,7 +58,6 @@ mod resource; mod recognizer; mod route; mod pipeline; -mod fs; mod server; mod channel; mod wsframe; @@ -68,6 +67,7 @@ mod h2; mod h1writer; mod h2writer; +pub mod fs; pub mod ws; pub mod dev; pub mod error; @@ -81,12 +81,11 @@ pub use application::Application; pub use httprequest::{HttpRequest, UrlEncoded}; pub use httpresponse::HttpResponse; pub use payload::{Payload, PayloadItem}; -pub use route::Reply; +pub use route::{Reply, FromRequest}; pub use resource::Resource; pub use recognizer::Params; pub use server::HttpServer; pub use context::HttpContext; -pub use fs::StaticFiles; // re-exports pub use http::{Method, StatusCode, Version}; diff --git a/src/route.rs b/src/route.rs index e949fd853..7a1fbcbc5 100644 --- a/src/route.rs +++ b/src/route.rs @@ -1,5 +1,4 @@ use std::marker::PhantomData; -use std::result::Result as StdResult; use actix::Actor; use futures::Future; @@ -21,7 +20,13 @@ pub trait Handler: 'static { } pub trait FromRequest { - fn from_request(self, req: HttpRequest) -> Reply; + /// The associated item which can be returned. + type Item: Into; + + /// The associated error which can be returned. + type Error: Into; + + fn from_request(self, req: HttpRequest) -> Result; } /// Handler for Fn() @@ -72,46 +77,53 @@ impl Reply { } impl FromRequest for Reply { - fn from_request(self, _: HttpRequest) -> Reply { - self + type Item = Reply; + type Error = Error; + + fn from_request(self, _: HttpRequest) -> Result { + Ok(self) } } impl FromRequest for HttpResponse { - fn from_request(self, _: HttpRequest) -> Reply { - Reply(ReplyItem::Message(self)) + type Item = Reply; + type Error = Error; + + fn from_request(self, _: HttpRequest) -> Result { + Ok(Reply(ReplyItem::Message(self))) } } -#[cfg(actix_nightly)] -default impl FromRequest for T -{ - fn from_request(self, req: HttpRequest) -> Reply { - self.from_request(req) +impl From for Reply { + + fn from(resp: HttpResponse) -> Reply { + Reply(ReplyItem::Message(resp)) } } -#[cfg(actix_nightly)] -default impl, E: Into> FromRequest for StdResult { - fn from_request(self, req: HttpRequest) -> Reply { +impl, E: Into> FromRequest for Result { + type Item = Reply; + type Error = E; + + fn from_request(self, _: HttpRequest) -> Result { match self { - Ok(val) => Reply(ReplyItem::Message(val.into())), //val.from_request(req), - Err(err) => Reply(ReplyItem::Message(err.into().into())), + Ok(val) => Ok(Reply(ReplyItem::Message(val.into()))), + Err(err) => Err(err), } } } -impl> FromRequest for StdResult { - fn from_request(self, _: HttpRequest) -> Reply { - match self { - Ok(val) => val, - Err(err) => Reply(ReplyItem::Message(err.into().into())), - } +impl> FromRequest for Result { + type Item = Reply; + type Error = E; + + fn from_request(self, _: HttpRequest) -> Result { + self } } -impl> From> for Reply { - fn from(res: StdResult) -> Self { +impl> From> for Reply { + fn from(res: Result) -> Self { match res { Ok(val) => val, Err(err) => Reply(ReplyItem::Message(err.into().into())), @@ -119,23 +131,18 @@ impl> From> for Reply { } } -impl> FromRequest for StdResult { - fn from_request(self, _: HttpRequest) -> Reply { - match self { - Ok(val) => Reply(ReplyItem::Message(val)), - Err(err) => Reply(ReplyItem::Message(err.into().into())), - } - } -} - impl>, S: 'static> FromRequest for HttpContext { - fn from_request(self, _: HttpRequest) -> Reply { - Reply(ReplyItem::Actor(Box::new(self))) + type Item = Reply; + type Error = Error; + + fn from_request(self, _: HttpRequest) -> Result { + Ok(Reply(ReplyItem::Actor(Box::new(self)))) } } impl>, S: 'static> From> for Reply { + fn from(ctx: HttpContext) -> Reply { Reply(ReplyItem::Actor(Box::new(ctx))) } @@ -143,8 +150,11 @@ impl>, S: 'static> From> fo impl FromRequest for Box> { - fn from_request(self, _: HttpRequest) -> Reply { - Reply(ReplyItem::Future(self)) + type Item = Reply; + type Error = Error; + + fn from_request(self, _: HttpRequest) -> Result { + Ok(Reply(ReplyItem::Future(self))) } } @@ -181,7 +191,10 @@ impl RouteHandler for WrapHandler { fn handle(&self, req: HttpRequest) -> Reply { let req2 = req.clone_without_state(); - self.h.handle(req).from_request(req2) + match self.h.handle(req).from_request(req2) { + Ok(reply) => reply.into(), + Err(err) => Reply::response(err.into()), + } } }