1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-24 07:53:00 +01:00

add Item and Error to FromRequest trait

This commit is contained in:
Nikolay Kim 2017-12-03 14:22:04 -08:00
parent 6bc7d60f52
commit 7c6faaa8e0
14 changed files with 178 additions and 108 deletions

View File

@ -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));

View File

@ -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);

View File

@ -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");

View File

@ -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();

View File

@ -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();

View File

@ -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<Reply, Into<Error>>` object.
* Function that accepts `HttpRequest` and return actor that has `HttpContext<A>`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<HttpResponse> 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<HttpResponse> {
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<Result<HttpResponse>> for MyObj {
fn into(self) -> Result<HttpResponse> {
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.

View File

@ -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<String> {
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<String> {
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();
}
```

View File

@ -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};

View File

@ -31,7 +31,7 @@ use httpcodes::{HTTPBadRequest, HTTPMethodNotAllowed, HTTPExpectationFailed};
pub type Result<T> = result::Result<T, Error>;
/// General purpose actix web error
#[derive(Debug)]
#[derive(Fail, Debug)]
pub struct Error {
cause: Box<ResponseError>,
}

View File

@ -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<D: Into<PathBuf>>(dir: D, index: bool) -> StaticFiles {
let dir = dir.into();

View File

@ -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<S> RouteHandler<S> 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<HttpResponse, HttpError> {
self.build().body(Body::Empty)
}
}

View File

@ -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<HttpResponseBuilder> 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<HttpResponse, HttpError> {
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, HttpError> {
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, HttpError> {
HttpResponse::build(StatusCode::OK)
.content_type("application/octet-stream")
.body(self)
.from_request(req)
}
}
@ -512,11 +518,13 @@ impl From<String> 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, HttpError> {
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, HttpError> {
HttpResponse::build(StatusCode::OK)
.content_type("text/plain; charset=utf-8")
.body(self)
.from_request(req)
}
}
@ -548,11 +558,13 @@ impl From<Bytes> 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, HttpError> {
HttpResponse::build(StatusCode::OK)
.content_type("application/octet-stream")
.body(self)
.from_request(req)
}
}
@ -566,11 +578,13 @@ impl From<BytesMut> 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, HttpError> {
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")));
}
}

View File

@ -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};

View File

@ -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<S>: 'static {
}
pub trait FromRequest {
fn from_request(self, req: HttpRequest) -> Reply;
/// The associated item which can be returned.
type Item: Into<Reply>;
/// The associated error which can be returned.
type Error: Into<Error>;
fn from_request(self, req: HttpRequest) -> Result<Self::Item, Self::Error>;
}
/// Handler<S> 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<Reply, Error> {
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<Reply, Error> {
Ok(Reply(ReplyItem::Message(self)))
}
}
#[cfg(actix_nightly)]
default impl<T: FromRequest> FromRequest for T
{
fn from_request(self, req: HttpRequest) -> Reply {
self.from_request(req)
impl From<HttpResponse> for Reply {
fn from(resp: HttpResponse) -> Reply {
Reply(ReplyItem::Message(resp))
}
}
#[cfg(actix_nightly)]
default impl<T: Into<HttpResponse>, E: Into<Error>> FromRequest for StdResult<T, E> {
fn from_request(self, req: HttpRequest) -> Reply {
impl<T: Into<HttpResponse>, E: Into<Error>> FromRequest for Result<T, E> {
type Item = Reply;
type Error = E;
fn from_request(self, _: HttpRequest) -> Result<Reply, Self::Error> {
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<E: Into<Error>> FromRequest for StdResult<Reply, E> {
fn from_request(self, _: HttpRequest) -> Reply {
match self {
Ok(val) => val,
Err(err) => Reply(ReplyItem::Message(err.into().into())),
}
impl<E: Into<Error>> FromRequest for Result<Reply, E> {
type Item = Reply;
type Error = E;
fn from_request(self, _: HttpRequest) -> Result<Reply, E> {
self
}
}
impl<E: Into<Error>> From<StdResult<Reply, E>> for Reply {
fn from(res: StdResult<Reply, E>) -> Self {
impl<E: Into<Error>> From<Result<Reply, E>> for Reply {
fn from(res: Result<Reply, E>) -> Self {
match res {
Ok(val) => val,
Err(err) => Reply(ReplyItem::Message(err.into().into())),
@ -119,23 +131,18 @@ impl<E: Into<Error>> From<StdResult<Reply, E>> for Reply {
}
}
impl<E: Into<Error>> FromRequest for StdResult<HttpResponse, E> {
fn from_request(self, _: HttpRequest) -> Reply {
match self {
Ok(val) => Reply(ReplyItem::Message(val)),
Err(err) => Reply(ReplyItem::Message(err.into().into())),
}
}
}
impl<A: Actor<Context=HttpContext<A, S>>, S: 'static> FromRequest for HttpContext<A, S>
{
fn from_request(self, _: HttpRequest) -> Reply {
Reply(ReplyItem::Actor(Box::new(self)))
type Item = Reply;
type Error = Error;
fn from_request(self, _: HttpRequest) -> Result<Reply, Error> {
Ok(Reply(ReplyItem::Actor(Box::new(self))))
}
}
impl<A: Actor<Context=HttpContext<A, S>>, S: 'static> From<HttpContext<A, S>> for Reply {
fn from(ctx: HttpContext<A, S>) -> Reply {
Reply(ReplyItem::Actor(Box::new(ctx)))
}
@ -143,8 +150,11 @@ impl<A: Actor<Context=HttpContext<A, S>>, S: 'static> From<HttpContext<A, S>> fo
impl FromRequest for Box<Future<Item=HttpResponse, Error=Error>>
{
fn from_request(self, _: HttpRequest) -> Reply {
Reply(ReplyItem::Future(self))
type Item = Reply;
type Error = Error;
fn from_request(self, _: HttpRequest) -> Result<Reply, Error> {
Ok(Reply(ReplyItem::Future(self)))
}
}
@ -181,7 +191,10 @@ impl<S, H, R> RouteHandler<S> for WrapHandler<S, H, R>
{
fn handle(&self, req: HttpRequest<S>) -> 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()),
}
}
}