use std::cell::RefCell; use std::rc::Rc; use actix_http::{Error, Response}; use actix_service::boxed::{self, BoxedNewService, BoxedService}; use actix_service::{ ApplyTransform, IntoNewService, IntoTransform, NewService, Service, Transform, }; use futures::future::{ok, Either, FutureResult}; use futures::{Async, Future, IntoFuture, Poll}; use crate::dev::{insert_slash, AppConfig, HttpServiceFactory, ResourceDef}; use crate::extract::FromRequest; use crate::guard::Guard; use crate::handler::{AsyncFactory, Factory}; use crate::responder::Responder; use crate::route::{CreateRouteService, Route, RouteService}; use crate::service::{ServiceRequest, ServiceResponse}; type HttpService
= BoxedService = BoxedNewService<(), ServiceRequest , ServiceResponse, (), ()>;
/// *Resource* is an entry in route table which corresponds to requested URL.
///
/// Resource in turn has at least one route.
/// Route consists of an handlers objects and list of guards
/// (objects that implement `Guard` trait).
/// Resources and rouets uses builder-like pattern for configuration.
/// During request handling, resource object iterate through all routes
/// and check guards for specific route, if request matches all
/// guards, route considered matched and route handler get called.
///
/// ```rust
/// use actix_web::{web, App, HttpResponse};
///
/// fn main() {
/// let app = App::new().service(
/// web::resource("/")
/// .route(web::get().to(|| HttpResponse::Ok())));
/// }
pub struct Resource > {
endpoint: T,
rdef: String,
routes: Vec Resource {
pub fn new(path: &str) -> Resource {
let fref = Rc::new(RefCell::new(None));
Resource {
routes: Vec::new(),
rdef: path.to_string(),
endpoint: ResourceEndpoint::new(fref.clone()),
factory_ref: fref,
guards: Vec::new(),
default: Rc::new(RefCell::new(None)),
}
}
}
impl Resource
where
P: 'static,
T: NewService<
ServiceRequest ,
Response = ServiceResponse,
Error = (),
InitError = (),
>,
{
/// Add match guard to a resource.
///
/// ```rust
/// use actix_web::{web, guard, App, HttpResponse};
///
/// fn index(data: web::Path<(String, String)>) -> &'static str {
/// "Welcome!"
/// }
///
/// fn main() {
/// let app = App::new()
/// .service(
/// web::resource("/app")
/// .guard(guard::Header("content-type", "text/plain"))
/// .route(web::get().to(index))
/// )
/// .service(
/// web::resource("/app")
/// .guard(guard::Header("content-type", "text/json"))
/// .route(web::get().to(|| HttpResponse::MethodNotAllowed()))
/// );
/// }
/// ```
pub fn guard ) -> Self {
self.routes.push(route.finish());
self
}
/// Register a new route and add handler. This route matches all requests.
///
/// ```rust
/// use actix_web::*;
///
/// fn index(req: HttpRequest) -> HttpResponse {
/// unimplemented!()
/// }
///
/// App::new().service(web::resource("/").to(index));
/// ```
///
/// This is shortcut for:
///
/// ```rust
/// # extern crate actix_web;
/// # use actix_web::*;
/// # fn index(req: HttpRequest) -> HttpResponse { unimplemented!() }
/// App::new().service(web::resource("/").route(web::route().to(index)));
/// ```
pub fn to + 'static,
R: Responder + 'static,
{
self.routes.push(Route::new().to(handler));
self
}
/// Register a new route and add async handler.
///
/// ```rust
/// use actix_web::*;
/// use futures::future::{ok, Future};
///
/// fn index(req: HttpRequest) -> impl Future + 'static,
R: IntoFuture + 'static,
R::Item: Into ,
Response = ServiceResponse,
Error = (),
InitError = (),
>,
>
where
M: Transform<
T::Service,
ServiceRequest ,
Response = ServiceResponse,
Error = (),
InitError = (),
>,
F: IntoTransform ) -> R,
R: IntoNewService>,
U: NewService HttpServiceFactory for Resource
where
P: 'static,
T: NewService<
ServiceRequest ,
Response = ServiceResponse,
Error = (),
InitError = (),
> + 'static,
{
fn register(mut self, config: &mut AppConfig ) {
if self.default.borrow().is_none() {
*self.default.borrow_mut() = Some(config.default_service());
}
let guards = if self.guards.is_empty() {
None
} else {
Some(std::mem::replace(&mut self.guards, Vec::new()))
};
let rdef = if config.is_root() || !self.rdef.is_empty() {
ResourceDef::new(&insert_slash(&self.rdef))
} else {
ResourceDef::new(&self.rdef)
};
config.register_service(rdef, guards, self)
}
}
impl IntoNewService
where
T: NewService<
ServiceRequest ,
Response = ServiceResponse,
Error = (),
InitError = (),
>,
{
fn into_new_service(self) -> T {
*self.factory_ref.borrow_mut() = Some(ResourceFactory {
routes: self.routes,
default: self.default,
});
self.endpoint
}
}
pub struct ResourceFactory {
routes: Vec {
type Response = ServiceResponse;
type Error = ();
type InitError = ();
type Service = ResourceService ;
type Future = CreateResourceService ;
fn new_service(&self, _: &()) -> Self::Future {
let default_fut = if let Some(ref default) = *self.default.borrow() {
Some(default.new_service(&()))
} else {
None
};
CreateResourceService {
fut: self
.routes
.iter()
.map(|route| CreateRouteServiceItem::Future(route.new_service(&())))
.collect(),
default: None,
default_fut,
}
}
}
enum CreateRouteServiceItem {
Future(CreateRouteService ),
Service(RouteService ),
}
pub struct CreateResourceService {
fut: Vec Future for CreateResourceService {
type Item = ResourceService ;
type Error = ();
fn poll(&mut self) -> Poll {
routes: Vec Service {
type Response = ServiceResponse;
type Error = ();
type Future = Either<
Box ) -> Self::Future {
for route in self.routes.iter_mut() {
if route.check(&mut req) {
return Either::A(route.call(req));
}
}
if let Some(ref mut default) = self.default {
Either::B(Either::A(default.call(req)))
} else {
let req = req.into_request();
Either::B(Either::B(ok(ServiceResponse::new(
req,
Response::NotFound().finish(),
))))
}
}
}
#[doc(hidden)]
pub struct ResourceEndpoint {
factory: Rc ResourceEndpoint {
fn new(factory: Rc {
type Response = ServiceResponse;
type Error = ();
type InitError = ();
type Service = ResourceService ;
type Future = CreateResourceService ;
fn new_service(&self, _: &()) -> Self::Future {
self.factory.borrow_mut().as_mut().unwrap().new_service(&())
}
}