1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-10-03 08:41:55 +00:00

refactor RouteHandler trait

This commit is contained in:
Nikolay Kim 2017-11-29 13:26:55 -08:00
parent 6f833798c7
commit 16ceb741b8
8 changed files with 144 additions and 169 deletions

View file

@ -82,8 +82,15 @@ fn main() {
.header("LOCATION", "/index.html")
.body(Body::Empty)
}))
.handler("/test", |req| {
match *req.method() {
Method::GET => httpcodes::HTTPOk,
Method::POST => httpcodes::HTTPMethodNotAllowed,
_ => httpcodes::HTTPNotFound,
}
})
// static files
.route_handler("/static", StaticFiles::new("examples/static/", true)))
.route("/static", StaticFiles::new("examples/static/", true)))
.serve::<_, ()>("127.0.0.1:8080").unwrap();
println!("Started http server: 127.0.0.1:8080");

View file

@ -66,7 +66,8 @@ fn main() {
.middleware(middlewares::Logger::default())
// websocket route
.resource("/ws/", |r| r.get(ws_index))
.route_handler("/", StaticFiles::new("examples/static/", true)))
// static files
.route("/", StaticFiles::new("examples/static/", true)))
// start http server on 127.0.0.1:8080
.serve::<_, ()>("127.0.0.1:8080").unwrap();

View file

@ -2,7 +2,7 @@ use std::rc::Rc;
use std::collections::HashMap;
use task::Task;
use route::{RouteHandler, FnHandler, Reply};
use route::{RouteHandler, WrapHandler, Reply, Handler};
use resource::Resource;
use recognizer::{RouteRecognizer, check_pattern};
use httprequest::HttpRequest;
@ -126,10 +126,7 @@ impl<S> ApplicationBuilder<S> where S: 'static {
/// store userid and friend in the exposed Params object:
///
/// ```rust
/// extern crate actix;
/// extern crate actix_web;
///
/// use actix::*;
/// use actix_web::*;
///
/// fn main() {
@ -158,7 +155,7 @@ impl<S> ApplicationBuilder<S> where S: 'static {
self
}
/// Default resource is used if no matches route could be found.
/// Default resource is used if no match route could be found.
pub fn default_resource<F>(&mut self, f: F) -> &mut Self
where F: FnOnce(&mut Resource<S>) + 'static
{
@ -178,7 +175,7 @@ impl<S> ApplicationBuilder<S> where S: 'static {
///
/// fn main() {
/// let app = Application::default("/")
/// .handler("/test", |req| {
/// .inline("/test", |req| {
/// match *req.method() {
/// Method::GET => httpcodes::HTTPOk,
/// Method::POST => httpcodes::HTTPMethodNotAllowed,
@ -189,28 +186,22 @@ impl<S> ApplicationBuilder<S> where S: 'static {
/// }
/// ```
pub fn handler<P, F, R>(&mut self, path: P, handler: F) -> &mut Self
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Into<Reply> + 'static,
P: Into<String>,
where P: Into<String>,
F: Fn(HttpRequest<S>) -> R + 'static,
R: Into<Reply> + 'static
{
self.parts.as_mut().expect("Use after finish")
.handlers.insert(path.into(), Box::new(FnHandler::new(handler)));
.handlers.insert(path.into(), Box::new(WrapHandler::new(handler)));
self
}
/// Add path handler
pub fn route_handler<H, P>(&mut self, path: P, h: H) -> &mut Self
where H: RouteHandler<S> + 'static, P: Into<String>
/// This method register handler for specified path prefix.
/// Any path that starts with this prefix matches handler.
pub fn route<P, H>(&mut self, path: P, handler: H) -> &mut Self
where P: Into<String>, H: Handler<S>
{
{
// add resource
let parts = self.parts.as_mut().expect("Use after finish");
let path = path.into();
if parts.handlers.contains_key(&path) {
panic!("Handler already registered: {:?}", path);
}
parts.handlers.insert(path, Box::new(h));
}
self.parts.as_mut().expect("Use after finish")
.handlers.insert(path.into(), Box::new(WrapHandler::new(handler)));
self
}

View file

@ -10,6 +10,7 @@
// dev specific
pub use task::Task;
pub use route::Handler;
pub use pipeline::Pipeline;
pub use recognizer::RouteRecognizer;
pub use channel::HttpChannel;

View file

@ -82,7 +82,7 @@ pub use application::Application;
pub use httprequest::{HttpRequest, UrlEncoded};
pub use httpresponse::HttpResponse;
pub use payload::{Payload, PayloadItem};
pub use route::{Frame, RouteHandler, Reply};
pub use route::{Frame, Reply};
pub use resource::Resource;
pub use recognizer::Params;
pub use server::HttpServer;

View file

@ -6,7 +6,7 @@ use futures::Stream;
use task::Task;
use error::Error;
use route::{Reply, RouteHandler, Frame, FnHandler, StreamHandler};
use route::{Reply, RouteHandler, Frame, WrapHandler, Handler, StreamHandler};
use httprequest::HttpRequest;
use httpcodes::{HTTPNotFound, HTTPMethodNotAllowed};
@ -17,13 +17,12 @@ use httpcodes::{HTTPNotFound, HTTPMethodNotAllowed};
/// Resource in turn has at least one route.
/// Route corresponds to handling HTTP method by calling route handler.
///
/// ```rust,ignore
///
/// struct MyRoute;
/// ```rust
/// extern crate actix_web;
///
/// fn main() {
/// let router = RoutingMap::default()
/// .resource("/", |r| r.post::<MyRoute>())
/// let app = actix_web::Application::default("/")
/// .resource("/", |r| r.get(|_| actix_web::HttpResponse::Ok()))
/// .finish();
/// }
pub struct Resource<S=()> {
@ -63,7 +62,7 @@ impl<S> Resource<S> where S: 'static {
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Into<Reply> + 'static,
{
self.routes.insert(method, Box::new(FnHandler::new(handler)));
self.routes.insert(method, Box::new(WrapHandler::new(handler)));
}
/// Register async handler for specified method.
@ -74,51 +73,42 @@ impl<S> Resource<S> where S: 'static {
self.routes.insert(method, Box::new(StreamHandler::new(handler)));
}
/// Register handler for specified method.
pub fn route_handler<H>(&mut self, method: Method, handler: H)
where H: RouteHandler<S>
{
self.routes.insert(method, Box::new(handler));
}
/// Default handler is used if no matched route found.
/// By default `HTTPMethodNotAllowed` is used.
pub fn default_handler<H>(&mut self, handler: H)
where H: RouteHandler<S>
pub fn default_handler<H>(&mut self, handler: H) where H: Handler<S>
{
self.default = Box::new(handler);
self.default = Box::new(WrapHandler::new(handler));
}
/// Handler for `GET` method.
pub fn get<F, R>(&mut self, handler: F)
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Into<Reply> + 'static, {
self.routes.insert(Method::GET, Box::new(FnHandler::new(handler)));
self.routes.insert(Method::GET, Box::new(WrapHandler::new(handler)));
}
/// Handler for `POST` method.
pub fn post<F, R>(&mut self, handler: F)
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Into<Reply> + 'static, {
self.routes.insert(Method::POST, Box::new(FnHandler::new(handler)));
self.routes.insert(Method::POST, Box::new(WrapHandler::new(handler)));
}
/// Handler for `PUT` method.
pub fn put<F, R>(&mut self, handler: F)
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Into<Reply> + 'static, {
self.routes.insert(Method::PUT, Box::new(FnHandler::new(handler)));
self.routes.insert(Method::PUT, Box::new(WrapHandler::new(handler)));
}
/// Handler for `DELETE` method.
pub fn delete<F, R>(&mut self, handler: F)
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Into<Reply> + 'static, {
self.routes.insert(Method::DELETE, Box::new(FnHandler::new(handler)));
self.routes.insert(Method::DELETE, Box::new(WrapHandler::new(handler)));
}
}
impl<S: 'static> RouteHandler<S> for Resource<S> {
fn handle(&self, req: HttpRequest<S>, task: &mut Task) {

View file

@ -4,15 +4,14 @@ use std::marker::PhantomData;
use std::result::Result as StdResult;
use actix::Actor;
// use http::{header, Version};
use futures::Stream;
use task::{Task, DrainFut, IoContext};
use body::Binary;
use error::{Error}; //, ExpectError, Result};
use error::Error;
use context::HttpContext;
use httprequest::HttpRequest;
use httpresponse::HttpResponse;
use task::{Task, DrainFut, IoContext};
#[doc(hidden)]
#[derive(Debug)]
@ -28,118 +27,32 @@ impl Frame {
}
}
/// Trait defines object that could be regestered as resource route
/// Trait defines object that could be regestered as route handler
#[allow(unused_variables)]
pub trait RouteHandler<S>: 'static {
pub trait Handler<S>: 'static {
type Result: Into<Reply>;
/// Handle request
fn handle(&self, req: HttpRequest<S>, task: &mut Task);
fn handle(&self, req: HttpRequest<S>) -> Self::Result;
/// Set route prefix
fn set_prefix(&mut self, prefix: String) {}
}
/*
/// Actors with ability to handle http requests.
#[allow(unused_variables)]
pub trait RouteState {
/// Shared state. State is shared with all routes within same application
/// and could be accessed with `HttpRequest::state()` method.
type State;
/// Handle `EXPECT` header. By default respones with `HTTP/1.1 100 Continue`
fn expect(req: &mut HttpRequest<Self::State>, ctx: &mut Self::Context) -> Result<()>
where Self: Actor<Context=HttpContext<Self>>
{
// handle expect header only for HTTP/1.1
if req.version() == Version::HTTP_11 {
if let Some(expect) = req.headers().get(header::EXPECT) {
if let Ok(expect) = expect.to_str() {
if expect.to_lowercase() == "100-continue" {
ctx.write("HTTP/1.1 100 Continue\r\n\r\n");
Ok(())
} else {
Err(ExpectError::UnknownExpect.into())
}
} else {
Err(ExpectError::Encoding.into())
}
} else {
Ok(())
}
} else {
Ok(())
}
}
/// Handle incoming request with http actor.
fn handle(req: HttpRequest<Self::State>) -> Result<Reply>
where Self: Default, Self: Actor<Context=HttpContext<Self>>
{
Ok(HttpContext::new(req, Self::default()).into())
}
}*/
/// Fn() route handler
pub(crate)
struct FnHandler<S, R, F>
/// Handler<S> for Fn()
impl<F, R, S> Handler<S> for F
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Into<Reply>,
S: 'static,
R: Into<Reply> + 'static
{
f: Box<F>,
s: PhantomData<S>,
}
type Result = R;
impl<S, R, F> FnHandler<S, R, F>
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Into<Reply> + 'static,
S: 'static,
{
pub fn new(f: F) -> Self {
FnHandler{f: Box::new(f), s: PhantomData}
fn handle(&self, req: HttpRequest<S>) -> R {
(self)(req)
}
}
impl<S, R, F> RouteHandler<S> for FnHandler<S, R, F>
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Into<Reply> + 'static,
S: 'static,
{
fn handle(&self, req: HttpRequest<S>, task: &mut Task) {
(self.f)(req).into().into(task)
}
}
/// Async route handler
pub(crate)
struct StreamHandler<S, R, F>
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Stream<Item=Frame, Error=Error> + 'static,
S: 'static,
{
f: Box<F>,
s: PhantomData<S>,
}
impl<S, R, F> StreamHandler<S, R, F>
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Stream<Item=Frame, Error=Error> + 'static,
S: 'static,
{
pub fn new(f: F) -> Self {
StreamHandler{f: Box::new(f), s: PhantomData}
}
}
impl<S, R, F> RouteHandler<S> for StreamHandler<S, R, F>
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Stream<Item=Frame, Error=Error> + 'static,
S: 'static,
{
fn handle(&self, req: HttpRequest<S>, task: &mut Task) {
task.stream((self.f)(req))
}
}
/// Represents response process.
pub struct Reply(ReplyItem);
enum ReplyItem {
Message(HttpResponse),
@ -147,11 +60,8 @@ enum ReplyItem {
Stream(Box<Stream<Item=Frame, Error=Error>>),
}
/// Represents response process.
pub struct Reply(ReplyItem);
impl Reply {
impl Reply
{
/// Create actor response
pub fn actor<A, S>(ctx: HttpContext<A, S>) -> Reply
where A: Actor<Context=HttpContext<A, S>>, S: 'static
@ -209,3 +119,78 @@ impl<A: Actor<Context=HttpContext<A, S>>, S: 'static> From<HttpContext<A, S>> fo
Reply(ReplyItem::Actor(Box::new(item)))
}
}
/// Trait defines object that could be regestered as resource route
pub(crate) trait RouteHandler<S>: 'static {
/// Handle request
fn handle(&self, req: HttpRequest<S>, task: &mut Task);
/// Set route prefix
fn set_prefix(&mut self, _prefix: String) {}
}
/// Route handler wrapper for Handler
pub(crate)
struct WrapHandler<S, H, R>
where H: Handler<S, Result=R>,
R: Into<Reply>,
S: 'static,
{
h: H,
s: PhantomData<S>,
}
impl<S, H, R> WrapHandler<S, H, R>
where H: Handler<S, Result=R>,
R: Into<Reply>,
S: 'static,
{
pub fn new(h: H) -> Self {
WrapHandler{h: h, s: PhantomData}
}
}
impl<S, H, R> RouteHandler<S> for WrapHandler<S, H, R>
where H: Handler<S, Result=R>,
R: Into<Reply> + 'static,
S: 'static,
{
fn handle(&self, req: HttpRequest<S>, task: &mut Task) {
self.h.handle(req).into().into(task)
}
fn set_prefix(&mut self, prefix: String) {
self.h.set_prefix(prefix)
}
}
/// Async route handler
pub(crate)
struct StreamHandler<S, R, F>
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Stream<Item=Frame, Error=Error> + 'static,
S: 'static,
{
f: Box<F>,
s: PhantomData<S>,
}
impl<S, R, F> StreamHandler<S, R, F>
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Stream<Item=Frame, Error=Error> + 'static,
S: 'static,
{
pub fn new(f: F) -> Self {
StreamHandler{f: Box::new(f), s: PhantomData}
}
}
impl<S, R, F> RouteHandler<S> for StreamHandler<S, R, F>
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Stream<Item=Frame, Error=Error> + 'static,
S: 'static,
{
fn handle(&self, req: HttpRequest<S>, task: &mut Task) {
task.stream((self.f)(req))
}
}

View file

@ -7,9 +7,8 @@ use std::fmt::Write;
use std::fs::{File, DirEntry};
use std::path::PathBuf;
use task::Task;
use route::RouteHandler;
use mime_guess::get_mime_type;
use route::Handler;
use httprequest::HttpRequest;
use httpresponse::HttpResponse;
use httpcodes::{HTTPOk, HTTPNotFound, HTTPForbidden};
@ -24,7 +23,7 @@ use httpcodes::{HTTPOk, HTTPNotFound, HTTPForbidden};
///
/// fn main() {
/// let app = Application::default("/")
/// .route_handler("/static", StaticFiles::new(".", true))
/// .handler("/static", StaticFiles::new(".", true))
/// .finish();
/// }
/// ```
@ -128,7 +127,8 @@ impl StaticFiles {
}
}
impl<S: 'static> RouteHandler<S> for StaticFiles {
impl<S> Handler<S> for StaticFiles {
type Result = Result<HttpResponse, io::Error>;
fn set_prefix(&mut self, prefix: String) {
if prefix != "/" {
@ -136,9 +136,9 @@ impl<S: 'static> RouteHandler<S> for StaticFiles {
}
}
fn handle(&self, req: HttpRequest<S>, task: &mut Task) {
fn handle(&self, req: HttpRequest<S>) -> Self::Result {
if !self.accessible {
task.reply(HTTPNotFound)
Ok(HTTPNotFound.into())
} else {
let mut hidden = false;
let filepath = req.path()[self.prefix.len()..]
@ -152,7 +152,7 @@ impl<S: 'static> RouteHandler<S> for StaticFiles {
// hidden file
if hidden {
task.reply(HTTPNotFound)
return Ok(HTTPNotFound.into())
}
// full filepath
@ -160,19 +160,19 @@ impl<S: 'static> RouteHandler<S> for StaticFiles {
let filename = match self.directory.join(&filepath[idx..]).canonicalize() {
Ok(fname) => fname,
Err(err) => return match err.kind() {
io::ErrorKind::NotFound => task.reply(HTTPNotFound),
io::ErrorKind::PermissionDenied => task.reply(HTTPForbidden),
_ => task.error(err),
io::ErrorKind::NotFound => Ok(HTTPNotFound.into()),
io::ErrorKind::PermissionDenied => Ok(HTTPForbidden.into()),
_ => Err(err),
}
};
if filename.is_dir() {
match self.index(&filepath[idx..], &filename) {
Ok(resp) => task.reply(resp),
Ok(resp) => Ok(resp),
Err(err) => match err.kind() {
io::ErrorKind::NotFound => task.reply(HTTPNotFound),
io::ErrorKind::PermissionDenied => task.reply(HTTPForbidden),
_ => task.error(err),
io::ErrorKind::NotFound => Ok(HTTPNotFound.into()),
io::ErrorKind::PermissionDenied => Ok(HTTPForbidden.into()),
_ => Err(err),
}
}
} else {
@ -185,9 +185,9 @@ impl<S: 'static> RouteHandler<S> for StaticFiles {
Ok(mut file) => {
let mut data = Vec::new();
let _ = file.read_to_end(&mut data);
task.reply(resp.body(data).unwrap())
Ok(resp.body(data).unwrap())
},
Err(err) => task.error(err),
Err(err) => Err(err),
}
}
}