use std::rc::Rc; use std::marker::PhantomData; use smallvec::SmallVec; use http::{Method, StatusCode}; use pred; use route::Route; use handler::{Reply, Handler, Responder, FromRequest}; use middleware::Middleware; use httprequest::HttpRequest; use httpresponse::HttpResponse; /// *Resource* is an entry in route table which corresponds to requested URL. /// /// Resource in turn has at least one route. /// Route consists of an object that implements `Handler` trait (handler) /// and list of predicates (objects that implement `Predicate` trait). /// Route uses builder-like pattern for configuration. /// During request handling, resource object iterate through all routes /// and check all predicates for specific route, if request matches all predicates route /// route considered matched and route handler get called. /// /// ```rust /// # extern crate actix_web; /// use actix_web::{App, HttpResponse, http}; /// /// fn main() { /// let app = App::new() /// .resource( /// "/", |r| r.method(http::Method::GET).f(|r| HttpResponse::Ok())) /// .finish(); /// } pub struct ResourceHandler<S=()> { name: String, state: PhantomData<S>, routes: SmallVec<[Route<S>; 3]>, middlewares: Rc<Vec<Box<Middleware<S>>>>, } impl<S> Default for ResourceHandler<S> { fn default() -> Self { ResourceHandler { name: String::new(), state: PhantomData, routes: SmallVec::new(), middlewares: Rc::new(Vec::new()) } } } impl<S> ResourceHandler<S> { pub(crate) fn default_not_found() -> Self { ResourceHandler { name: String::new(), state: PhantomData, routes: SmallVec::new(), middlewares: Rc::new(Vec::new()) } } /// Set resource name pub fn name<T: Into<String>>(&mut self, name: T) { self.name = name.into(); } pub(crate) fn get_name(&self) -> &str { &self.name } } impl<S: 'static> ResourceHandler<S> { /// Register a new route and return mutable reference to *Route* object. /// *Route* is used for route configuration, i.e. adding predicates, setting up handler. /// /// ```rust /// # extern crate actix_web; /// use actix_web::*; /// /// fn main() { /// let app = App::new() /// .resource( /// "/", |r| r.route() /// .filter(pred::Any(pred::Get()).or(pred::Put())) /// .filter(pred::Header("Content-Type", "text/plain")) /// .f(|r| HttpResponse::Ok())) /// .finish(); /// } /// ``` pub fn route(&mut self) -> &mut Route<S> { self.routes.push(Route::default()); self.routes.last_mut().unwrap() } /// Register a new route and add method check to route. /// /// This is shortcut for: /// /// ```rust,ignore /// Application::resource("/", |r| r.route().filter(pred::Get()).f(index) /// ``` pub fn method(&mut self, method: Method) -> &mut Route<S> { self.routes.push(Route::default()); self.routes.last_mut().unwrap().filter(pred::Method(method)) } /// Register a new route and add handler object. /// /// This is shortcut for: /// /// ```rust,ignore /// Application::resource("/", |r| r.route().h(handler) /// ``` pub fn h<H: Handler<S>>(&mut self, handler: H) { self.routes.push(Route::default()); self.routes.last_mut().unwrap().h(handler) } /// Register a new route and add handler function. /// /// This is shortcut for: /// /// ```rust,ignore /// Application::resource("/", |r| r.route().f(index) /// ``` pub fn f<F, R>(&mut self, handler: F) where F: Fn(HttpRequest<S>) -> R + 'static, R: Responder + 'static, { self.routes.push(Route::default()); self.routes.last_mut().unwrap().f(handler) } /// Register a new route and add handler. /// /// This is shortcut for: /// /// ```rust,ignore /// Application::resource("/", |r| r.route().with(index) /// ``` pub fn with<T, F, R>(&mut self, handler: F) where F: Fn(T) -> R + 'static, R: Responder + 'static, T: FromRequest<S> + 'static, { self.routes.push(Route::default()); self.routes.last_mut().unwrap().with(handler); } /// Register a resource middleware /// /// This is similar to `App's` middlewares, but /// middlewares get invoked on resource level. pub fn middleware<M: Middleware<S>>(&mut self, mw: M) { Rc::get_mut(&mut self.middlewares).unwrap().push(Box::new(mw)); } pub(crate) fn handle(&mut self, mut req: HttpRequest<S>, default: Option<&mut ResourceHandler<S>>) -> Reply { for route in &mut self.routes { if route.check(&mut req) { return if self.middlewares.is_empty() { route.handle(req) } else { route.compose(req, Rc::clone(&self.middlewares)) }; } } if let Some(resource) = default { resource.handle(req, None) } else { Reply::response(HttpResponse::new(StatusCode::NOT_FOUND)) } } }