mirror of
https://github.com/actix/actix-web.git
synced 2024-11-30 05:21:09 +00:00
refactor service registration process; unify services and resources
This commit is contained in:
parent
5cde4dc479
commit
fe22e83144
18 changed files with 845 additions and 779 deletions
|
@ -174,7 +174,7 @@ impl CookieSessionInner {
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use actix_session::CookieSession;
|
/// use actix_session::CookieSession;
|
||||||
/// use actix_web::{App, HttpResponse, HttpServer};
|
/// use actix_web::{web, App, HttpResponse, HttpServer};
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().middleware(
|
/// let app = App::new().middleware(
|
||||||
|
@ -183,7 +183,7 @@ impl CookieSessionInner {
|
||||||
/// .name("actix_session")
|
/// .name("actix_session")
|
||||||
/// .path("/")
|
/// .path("/")
|
||||||
/// .secure(true))
|
/// .secure(true))
|
||||||
/// .resource("/", |r| r.to(|| HttpResponse::Ok()));
|
/// .service(web::resource("/").to(|| HttpResponse::Ok()));
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct CookieSession(Rc<CookieSessionInner>);
|
pub struct CookieSession(Rc<CookieSessionInner>);
|
||||||
|
@ -314,19 +314,17 @@ where
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use actix_web::{test, App};
|
use actix_web::{test, web, App};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cookie_session() {
|
fn cookie_session() {
|
||||||
let mut app = test::init_service(
|
let mut app = test::init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.middleware(CookieSession::signed(&[0; 32]).secure(false))
|
.middleware(CookieSession::signed(&[0; 32]).secure(false))
|
||||||
.resource("/", |r| {
|
.service(web::resource("/").to(|ses: Session| {
|
||||||
r.to(|ses: Session| {
|
let _ = ses.set("counter", 100);
|
||||||
let _ = ses.set("counter", 100);
|
"test"
|
||||||
"test"
|
})),
|
||||||
})
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let request = test::TestRequest::get().to_request();
|
let request = test::TestRequest::get().to_request();
|
||||||
|
@ -342,12 +340,10 @@ mod tests {
|
||||||
let mut app = test::init_service(
|
let mut app = test::init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.middleware(CookieSession::signed(&[0; 32]).secure(false))
|
.middleware(CookieSession::signed(&[0; 32]).secure(false))
|
||||||
.resource("/", |r| {
|
.service(web::resource("/").to(|ses: Session| {
|
||||||
r.to(|ses: Session| {
|
let _ = ses.set("counter", 100);
|
||||||
let _ = ses.set("counter", 100);
|
"test"
|
||||||
"test"
|
})),
|
||||||
})
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let request = test::TestRequest::get().to_request();
|
let request = test::TestRequest::get().to_request();
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
//! extractor allows us to get or set session data.
|
//! extractor allows us to get or set session data.
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use actix_web::{App, HttpServer, HttpResponse, Error};
|
//! use actix_web::{web, App, HttpServer, HttpResponse, Error};
|
||||||
//! use actix_session::{Session, CookieSession};
|
//! use actix_session::{Session, CookieSession};
|
||||||
//!
|
//!
|
||||||
//! fn index(session: Session) -> Result<&'static str, Error> {
|
//! fn index(session: Session) -> Result<&'static str, Error> {
|
||||||
|
@ -29,19 +29,17 @@
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! fn main() -> std::io::Result<()> {
|
//! fn main() -> std::io::Result<()> {
|
||||||
//! let sys = actix_rt::System::new("example"); // <- create Actix runtime
|
//! # std::thread::spawn(||
|
||||||
//!
|
|
||||||
//! HttpServer::new(
|
//! HttpServer::new(
|
||||||
//! || App::new().middleware(
|
//! || App::new().middleware(
|
||||||
//! CookieSession::signed(&[0; 32]) // <- create cookie based session middleware
|
//! CookieSession::signed(&[0; 32]) // <- create cookie based session middleware
|
||||||
//! .secure(false)
|
//! .secure(false)
|
||||||
//! )
|
//! )
|
||||||
//! .resource("/", |r| r.to(|| HttpResponse::Ok())))
|
//! .service(web::resource("/").to(|| HttpResponse::Ok())))
|
||||||
//! .bind("127.0.0.1:59880")?
|
//! .bind("127.0.0.1:59880")?
|
||||||
//! .start();
|
//! .run()
|
||||||
//! # actix_rt::System::current().stop();
|
//! # );
|
||||||
//! sys.run();
|
//! # Ok(())
|
||||||
//! Ok(())
|
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
|
@ -17,7 +17,7 @@ use v_htmlescape::escape as escape_html_entity;
|
||||||
|
|
||||||
use actix_http::error::{Error, ErrorInternalServerError};
|
use actix_http::error::{Error, ErrorInternalServerError};
|
||||||
use actix_service::{boxed::BoxedNewService, NewService, Service};
|
use actix_service::{boxed::BoxedNewService, NewService, Service};
|
||||||
use actix_web::dev::{self, HttpServiceFactory, ResourceDef, Url};
|
use actix_web::dev::{self, AppConfig, HttpServiceFactory, ResourceDef, Url};
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
blocking, FromRequest, HttpRequest, HttpResponse, Responder, ServiceFromRequest,
|
blocking, FromRequest, HttpRequest, HttpResponse, Responder, ServiceFromRequest,
|
||||||
ServiceRequest, ServiceResponse,
|
ServiceRequest, ServiceResponse,
|
||||||
|
@ -226,7 +226,7 @@ fn directory_listing(
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct StaticFiles<S, C = DefaultConfig> {
|
pub struct StaticFiles<S, C = DefaultConfig> {
|
||||||
path: ResourceDef,
|
path: String,
|
||||||
directory: PathBuf,
|
directory: PathBuf,
|
||||||
index: Option<String>,
|
index: Option<String>,
|
||||||
show_index: bool,
|
show_index: bool,
|
||||||
|
@ -259,7 +259,7 @@ impl<S: 'static, C: StaticFileConfig> StaticFiles<S, C> {
|
||||||
}
|
}
|
||||||
|
|
||||||
StaticFiles {
|
StaticFiles {
|
||||||
path: ResourceDef::root_prefix(path),
|
path: path.to_string(),
|
||||||
directory: dir,
|
directory: dir,
|
||||||
index: None,
|
index: None,
|
||||||
show_index: false,
|
show_index: false,
|
||||||
|
@ -300,15 +300,21 @@ impl<S: 'static, C: StaticFileConfig> StaticFiles<S, C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P, C: StaticFileConfig + 'static> HttpServiceFactory<P> for StaticFiles<P, C> {
|
impl<P, C> HttpServiceFactory<P> for StaticFiles<P, C>
|
||||||
type Factory = Self;
|
where
|
||||||
|
P: 'static,
|
||||||
fn rdef(&self) -> &ResourceDef {
|
C: StaticFileConfig + 'static,
|
||||||
&self.path
|
{
|
||||||
}
|
fn register(self, config: &mut AppConfig<P>) {
|
||||||
|
if self.default.borrow().is_none() {
|
||||||
fn create(self) -> Self {
|
*self.default.borrow_mut() = Some(config.default_service());
|
||||||
self
|
}
|
||||||
|
let rdef = if config.is_root() {
|
||||||
|
ResourceDef::root_prefix(&self.path)
|
||||||
|
} else {
|
||||||
|
ResourceDef::prefix(&self.path)
|
||||||
|
};
|
||||||
|
config.register_service(rdef, None, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,23 +27,23 @@ fn main() -> std::io::Result<()> {
|
||||||
App::new()
|
App::new()
|
||||||
.middleware(middleware::DefaultHeaders::new().header("X-Version", "0.2"))
|
.middleware(middleware::DefaultHeaders::new().header("X-Version", "0.2"))
|
||||||
.middleware(middleware::Compress::default())
|
.middleware(middleware::Compress::default())
|
||||||
.resource("/resource1/index.html", |r| r.route(web::get().to(index)))
|
.service(web::resource("/resource1/index.html").route(web::get().to(index)))
|
||||||
.resource("/resource2/index.html", |r| {
|
.service(
|
||||||
r.middleware(
|
web::resource("/resource2/index.html")
|
||||||
middleware::DefaultHeaders::new().header("X-Version-R2", "0.3"),
|
.middleware(
|
||||||
)
|
middleware::DefaultHeaders::new().header("X-Version-R2", "0.3"),
|
||||||
.default_resource(|r| {
|
)
|
||||||
r.route(web::route().to(|| HttpResponse::MethodNotAllowed()))
|
.default_resource(|r| {
|
||||||
})
|
r.route(web::route().to(|| HttpResponse::MethodNotAllowed()))
|
||||||
.route(web::method(Method::GET).to_async(index_async))
|
})
|
||||||
})
|
.route(web::method(Method::GET).to_async(index_async)),
|
||||||
.resource("/test1.html", |r| r.to(|| "Test\r\n"))
|
)
|
||||||
.resource("/", |r| r.to(no_params))
|
.service(web::resource("/test1.html").to(|| "Test\r\n"))
|
||||||
|
.service(web::resource("/").to(no_params))
|
||||||
})
|
})
|
||||||
.bind("127.0.0.1:8080")?
|
.bind("127.0.0.1:8080")?
|
||||||
.workers(1)
|
.workers(1)
|
||||||
.start();
|
.start();
|
||||||
|
|
||||||
let _ = sys.run();
|
sys.run()
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
420
src/app.rs
420
src/app.rs
|
@ -7,16 +7,20 @@ use actix_http::{Extensions, PayloadStream, Request, Response};
|
||||||
use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url};
|
use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url};
|
||||||
use actix_service::boxed::{self, BoxedNewService, BoxedService};
|
use actix_service::boxed::{self, BoxedNewService, BoxedService};
|
||||||
use actix_service::{
|
use actix_service::{
|
||||||
AndThenNewService, ApplyTransform, IntoNewService, IntoTransform, NewService,
|
fn_service, AndThenNewService, ApplyTransform, IntoNewService, IntoTransform,
|
||||||
Service, Transform,
|
NewService, Service, Transform,
|
||||||
};
|
};
|
||||||
use futures::future::{ok, Either, FutureResult};
|
use futures::future::{ok, Either, FutureResult};
|
||||||
use futures::{Async, Future, IntoFuture, Poll};
|
use futures::{Async, Future, IntoFuture, Poll};
|
||||||
|
|
||||||
|
use crate::config::AppConfig;
|
||||||
use crate::guard::Guard;
|
use crate::guard::Guard;
|
||||||
use crate::resource::Resource;
|
use crate::resource::Resource;
|
||||||
use crate::scope::{insert_slash, Scope};
|
use crate::route::Route;
|
||||||
use crate::service::{ServiceRequest, ServiceResponse};
|
use crate::service::{
|
||||||
|
HttpServiceFactory, ServiceFactory, ServiceFactoryWrapper, ServiceRequest,
|
||||||
|
ServiceResponse,
|
||||||
|
};
|
||||||
use crate::state::{State, StateFactory, StateFactoryResult};
|
use crate::state::{State, StateFactory, StateFactoryResult};
|
||||||
|
|
||||||
type Guards = Vec<Box<Guard>>;
|
type Guards = Vec<Box<Guard>>;
|
||||||
|
@ -24,19 +28,6 @@ type HttpService<P> = BoxedService<ServiceRequest<P>, ServiceResponse, ()>;
|
||||||
type HttpNewService<P> = BoxedNewService<(), ServiceRequest<P>, ServiceResponse, (), ()>;
|
type HttpNewService<P> = BoxedNewService<(), ServiceRequest<P>, ServiceResponse, (), ()>;
|
||||||
type BoxedResponse = Box<Future<Item = ServiceResponse, Error = ()>>;
|
type BoxedResponse = Box<Future<Item = ServiceResponse, Error = ()>>;
|
||||||
|
|
||||||
pub trait HttpServiceFactory<P> {
|
|
||||||
type Factory: NewService<
|
|
||||||
ServiceRequest<P>,
|
|
||||||
Response = ServiceResponse,
|
|
||||||
Error = (),
|
|
||||||
InitError = (),
|
|
||||||
>;
|
|
||||||
|
|
||||||
fn rdef(&self) -> &ResourceDef;
|
|
||||||
|
|
||||||
fn create(self) -> Self::Factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Application builder - structure that follows the builder pattern
|
/// Application builder - structure that follows the builder pattern
|
||||||
/// for building application instances.
|
/// for building application instances.
|
||||||
pub struct App<P, T>
|
pub struct App<P, T>
|
||||||
|
@ -97,9 +88,9 @@ where
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new()
|
/// let app = App::new()
|
||||||
/// .state(MyState{ counter: Cell::new(0) })
|
/// .state(MyState{ counter: Cell::new(0) })
|
||||||
/// .resource(
|
/// .service(
|
||||||
/// "/index.html",
|
/// web::resource("/index.html").route(
|
||||||
/// |r| r.route(web::get().to(index)));
|
/// web::get().to(index)));
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn state<S: 'static>(mut self, state: S) -> Self {
|
pub fn state<S: 'static>(mut self, state: S) -> Self {
|
||||||
|
@ -120,112 +111,6 @@ where
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configure scope for common root path.
|
|
||||||
///
|
|
||||||
/// Scopes collect multiple paths under a common path prefix.
|
|
||||||
/// Scope path can contain variable path segments as resources.
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # extern crate actix_web;
|
|
||||||
/// use actix_web::{App, HttpRequest, HttpResponse};
|
|
||||||
///
|
|
||||||
/// fn main() {
|
|
||||||
/// let app = App::new().scope("/{project_id}", |scope| {
|
|
||||||
/// scope
|
|
||||||
/// .resource("/path1", |r| r.to(|| HttpResponse::Ok()))
|
|
||||||
/// .resource("/path2", |r| r.to(|| HttpResponse::Ok()))
|
|
||||||
/// .resource("/path3", |r| r.to(|| HttpResponse::MethodNotAllowed()))
|
|
||||||
/// });
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// In the above example, three routes get added:
|
|
||||||
/// * /{project_id}/path1
|
|
||||||
/// * /{project_id}/path2
|
|
||||||
/// * /{project_id}/path3
|
|
||||||
///
|
|
||||||
pub fn scope<F>(self, path: &str, f: F) -> AppRouter<T, P, Body, AppEntry<P>>
|
|
||||||
where
|
|
||||||
F: FnOnce(Scope<P>) -> Scope<P>,
|
|
||||||
{
|
|
||||||
let mut scope = f(Scope::new(path));
|
|
||||||
let rdef = scope.rdef().clone();
|
|
||||||
let default = scope.get_default();
|
|
||||||
let guards = scope.take_guards();
|
|
||||||
|
|
||||||
let fref = Rc::new(RefCell::new(None));
|
|
||||||
AppRouter {
|
|
||||||
chain: self.chain,
|
|
||||||
services: vec![(rdef, boxed::new_service(scope.into_new_service()), guards)],
|
|
||||||
default: None,
|
|
||||||
defaults: vec![default],
|
|
||||||
endpoint: AppEntry::new(fref.clone()),
|
|
||||||
factory_ref: fref,
|
|
||||||
extensions: self.extensions,
|
|
||||||
state: self.state,
|
|
||||||
_t: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configure resource for a specific path.
|
|
||||||
///
|
|
||||||
/// Resources may have variable path segments. For example, a
|
|
||||||
/// resource with the path `/a/{name}/c` would match all incoming
|
|
||||||
/// requests with paths such as `/a/b/c`, `/a/1/c`, or `/a/etc/c`.
|
|
||||||
///
|
|
||||||
/// A variable segment is specified in the form `{identifier}`,
|
|
||||||
/// where the identifier can be used later in a request handler to
|
|
||||||
/// access the matched value for that segment. This is done by
|
|
||||||
/// looking up the identifier in the `Params` object returned by
|
|
||||||
/// `HttpRequest.match_info()` method.
|
|
||||||
///
|
|
||||||
/// By default, each segment matches the regular expression `[^{}/]+`.
|
|
||||||
///
|
|
||||||
/// You can also specify a custom regex in the form `{identifier:regex}`:
|
|
||||||
///
|
|
||||||
/// For instance, to route `GET`-requests on any route matching
|
|
||||||
/// `/users/{userid}/{friend}` and store `userid` and `friend` in
|
|
||||||
/// the exposed `Params` object:
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # extern crate actix_web;
|
|
||||||
/// use actix_web::{web, http, App, HttpResponse};
|
|
||||||
///
|
|
||||||
/// fn main() {
|
|
||||||
/// let app = App::new().resource("/users/{userid}/{friend}", |r| {
|
|
||||||
/// r.route(web::get().to(|| HttpResponse::Ok()))
|
|
||||||
/// .route(web::head().to(|| HttpResponse::MethodNotAllowed()))
|
|
||||||
/// });
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub fn resource<F, U>(self, path: &str, f: F) -> AppRouter<T, P, Body, AppEntry<P>>
|
|
||||||
where
|
|
||||||
F: FnOnce(Resource<P>) -> Resource<P, U>,
|
|
||||||
U: NewService<
|
|
||||||
ServiceRequest<P>,
|
|
||||||
Response = ServiceResponse,
|
|
||||||
Error = (),
|
|
||||||
InitError = (),
|
|
||||||
> + 'static,
|
|
||||||
{
|
|
||||||
let rdef = ResourceDef::new(&insert_slash(path));
|
|
||||||
let res = f(Resource::new());
|
|
||||||
let default = res.get_default();
|
|
||||||
|
|
||||||
let fref = Rc::new(RefCell::new(None));
|
|
||||||
AppRouter {
|
|
||||||
chain: self.chain,
|
|
||||||
services: vec![(rdef, boxed::new_service(res.into_new_service()), None)],
|
|
||||||
default: None,
|
|
||||||
defaults: vec![default],
|
|
||||||
endpoint: AppEntry::new(fref.clone()),
|
|
||||||
factory_ref: fref,
|
|
||||||
extensions: self.extensions,
|
|
||||||
state: self.state,
|
|
||||||
_t: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register a middleware.
|
/// Register a middleware.
|
||||||
pub fn middleware<M, B, F>(
|
pub fn middleware<M, B, F>(
|
||||||
self,
|
self,
|
||||||
|
@ -259,7 +144,6 @@ where
|
||||||
state: self.state,
|
state: self.state,
|
||||||
services: Vec::new(),
|
services: Vec::new(),
|
||||||
default: None,
|
default: None,
|
||||||
defaults: Vec::new(),
|
|
||||||
factory_ref: fref,
|
factory_ref: fref,
|
||||||
extensions: self.extensions,
|
extensions: self.extensions,
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
|
@ -298,25 +182,52 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register resource handler service.
|
/// Configure route for a specific path.
|
||||||
|
///
|
||||||
|
/// This is a simplified version of the `App::service()` method.
|
||||||
|
/// This method can not be could multiple times, in that case
|
||||||
|
/// multiple resources with one route would be registered for same resource path.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use actix_web::{web, App, HttpResponse, extract::Path};
|
||||||
|
///
|
||||||
|
/// fn index(data: Path<(String, String)>) -> &'static str {
|
||||||
|
/// "Welcome!"
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let app = App::new()
|
||||||
|
/// .route("/test1", web::get().to(index))
|
||||||
|
/// .route("/test2", web::post().to(|| HttpResponse::MethodNotAllowed()));
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn route(
|
||||||
|
self,
|
||||||
|
path: &str,
|
||||||
|
mut route: Route<P>,
|
||||||
|
) -> AppRouter<T, P, Body, AppEntry<P>> {
|
||||||
|
self.service(
|
||||||
|
Resource::new(path)
|
||||||
|
.add_guards(route.take_guards())
|
||||||
|
.route(route),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register http service.
|
||||||
pub fn service<F>(self, service: F) -> AppRouter<T, P, Body, AppEntry<P>>
|
pub fn service<F>(self, service: F) -> AppRouter<T, P, Body, AppEntry<P>>
|
||||||
where
|
where
|
||||||
F: HttpServiceFactory<P> + 'static,
|
F: HttpServiceFactory<P> + 'static,
|
||||||
{
|
{
|
||||||
let fref = Rc::new(RefCell::new(None));
|
let fref = Rc::new(RefCell::new(None));
|
||||||
|
|
||||||
AppRouter {
|
AppRouter {
|
||||||
chain: self.chain,
|
chain: self.chain,
|
||||||
services: vec![(
|
|
||||||
service.rdef().clone(),
|
|
||||||
boxed::new_service(service.create().map_init_err(|_| ())),
|
|
||||||
None,
|
|
||||||
)],
|
|
||||||
default: None,
|
default: None,
|
||||||
defaults: vec![],
|
|
||||||
endpoint: AppEntry::new(fref.clone()),
|
endpoint: AppEntry::new(fref.clone()),
|
||||||
factory_ref: fref,
|
factory_ref: fref,
|
||||||
extensions: self.extensions,
|
extensions: self.extensions,
|
||||||
state: self.state,
|
state: self.state,
|
||||||
|
services: vec![Box::new(ServiceFactoryWrapper::new(service))],
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -338,10 +249,9 @@ where
|
||||||
/// for building application instances.
|
/// for building application instances.
|
||||||
pub struct AppRouter<C, P, B, T> {
|
pub struct AppRouter<C, P, B, T> {
|
||||||
chain: C,
|
chain: C,
|
||||||
services: Vec<(ResourceDef, HttpNewService<P>, Option<Guards>)>,
|
|
||||||
default: Option<Rc<HttpNewService<P>>>,
|
|
||||||
defaults: Vec<Rc<RefCell<Option<Rc<HttpNewService<P>>>>>>,
|
|
||||||
endpoint: T,
|
endpoint: T,
|
||||||
|
services: Vec<Box<ServiceFactory<P>>>,
|
||||||
|
default: Option<Rc<HttpNewService<P>>>,
|
||||||
factory_ref: Rc<RefCell<Option<AppRoutingFactory<P>>>>,
|
factory_ref: Rc<RefCell<Option<AppRoutingFactory<P>>>>,
|
||||||
extensions: Extensions,
|
extensions: Extensions,
|
||||||
state: Vec<Box<StateFactory>>,
|
state: Vec<Box<StateFactory>>,
|
||||||
|
@ -359,131 +269,48 @@ where
|
||||||
InitError = (),
|
InitError = (),
|
||||||
>,
|
>,
|
||||||
{
|
{
|
||||||
/// Configure scope for common root path.
|
/// Configure route for a specific path.
|
||||||
///
|
///
|
||||||
/// Scopes collect multiple paths under a common path prefix.
|
/// This is a simplified version of the `App::service()` method.
|
||||||
/// Scope path can contain variable path segments as resources.
|
/// This method can not be could multiple times, in that case
|
||||||
|
/// multiple resources with one route would be registered for same resource path.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # extern crate actix_web;
|
/// use actix_web::{web, App, HttpResponse, extract::Path};
|
||||||
/// use actix_web::{App, HttpRequest, HttpResponse};
|
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn index(data: Path<(String, String)>) -> &'static str {
|
||||||
/// let app = App::new().scope("/{project_id}", |scope| {
|
/// "Welcome!"
|
||||||
/// scope
|
|
||||||
/// .resource("/path1", |r| r.to(|| HttpResponse::Ok()))
|
|
||||||
/// .resource("/path2", |r| r.to(|| HttpResponse::Ok()))
|
|
||||||
/// .resource("/path3", |r| r.to(|| HttpResponse::MethodNotAllowed()))
|
|
||||||
/// });
|
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// In the above example, three routes get added:
|
|
||||||
/// * /{project_id}/path1
|
|
||||||
/// * /{project_id}/path2
|
|
||||||
/// * /{project_id}/path3
|
|
||||||
///
|
|
||||||
pub fn scope<F>(mut self, path: &str, f: F) -> Self
|
|
||||||
where
|
|
||||||
F: FnOnce(Scope<P>) -> Scope<P>,
|
|
||||||
{
|
|
||||||
let mut scope = f(Scope::new(path));
|
|
||||||
let rdef = scope.rdef().clone();
|
|
||||||
let guards = scope.take_guards();
|
|
||||||
self.defaults.push(scope.get_default());
|
|
||||||
self.services
|
|
||||||
.push((rdef, boxed::new_service(scope.into_new_service()), guards));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configure resource for a specific path.
|
|
||||||
///
|
|
||||||
/// Resources may have variable path segments. For example, a
|
|
||||||
/// resource with the path `/a/{name}/c` would match all incoming
|
|
||||||
/// requests with paths such as `/a/b/c`, `/a/1/c`, or `/a/etc/c`.
|
|
||||||
///
|
|
||||||
/// A variable segment is specified in the form `{identifier}`,
|
|
||||||
/// where the identifier can be used later in a request handler to
|
|
||||||
/// access the matched value for that segment. This is done by
|
|
||||||
/// looking up the identifier in the `Params` object returned by
|
|
||||||
/// `HttpRequest.match_info()` method.
|
|
||||||
///
|
|
||||||
/// By default, each segment matches the regular expression `[^{}/]+`.
|
|
||||||
///
|
|
||||||
/// You can also specify a custom regex in the form `{identifier:regex}`:
|
|
||||||
///
|
|
||||||
/// For instance, to route `GET`-requests on any route matching
|
|
||||||
/// `/users/{userid}/{friend}` and store `userid` and `friend` in
|
|
||||||
/// the exposed `Params` object:
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use actix_web::{web, http, App, HttpResponse};
|
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new()
|
/// let app = App::new()
|
||||||
/// .resource("/users/{userid}/{friend}", |r| {
|
/// .route("/test1", web::get().to(index))
|
||||||
/// r.route(web::to(|| HttpResponse::Ok()))
|
/// .route("/test2", web::post().to(|| HttpResponse::MethodNotAllowed()));
|
||||||
/// })
|
|
||||||
/// .resource("/index.html", |r| {
|
|
||||||
/// r.route(web::head().to(|| HttpResponse::MethodNotAllowed()))
|
|
||||||
/// });
|
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn resource<F, U>(mut self, path: &str, f: F) -> Self
|
pub fn route(self, path: &str, mut route: Route<P>) -> Self {
|
||||||
where
|
self.service(
|
||||||
F: FnOnce(Resource<P>) -> Resource<P, U>,
|
Resource::new(path)
|
||||||
U: NewService<
|
.add_guards(route.take_guards())
|
||||||
ServiceRequest<P>,
|
.route(route),
|
||||||
Response = ServiceResponse,
|
)
|
||||||
Error = (),
|
|
||||||
InitError = (),
|
|
||||||
> + 'static,
|
|
||||||
{
|
|
||||||
let rdef = ResourceDef::new(&insert_slash(path));
|
|
||||||
let resource = f(Resource::new());
|
|
||||||
self.defaults.push(resource.get_default());
|
|
||||||
self.services.push((
|
|
||||||
rdef,
|
|
||||||
boxed::new_service(resource.into_new_service()),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Default resource to be used if no matching route could be found.
|
/// Register http service.
|
||||||
///
|
///
|
||||||
/// Default resource works with resources only and does not work with
|
/// Http service is any type that implements `HttpServiceFactory` trait.
|
||||||
/// custom services.
|
///
|
||||||
pub fn default_resource<F, U>(mut self, f: F) -> Self
|
/// Actix web provides several services implementations:
|
||||||
where
|
///
|
||||||
F: FnOnce(Resource<P>) -> Resource<P, U>,
|
/// * *Resource* is an entry in route table which corresponds to requested URL.
|
||||||
U: NewService<
|
/// * *Scope* is a set of resources with common root path.
|
||||||
ServiceRequest<P>,
|
/// * "StaticFiles" is a service for static files support
|
||||||
Response = ServiceResponse,
|
|
||||||
Error = (),
|
|
||||||
InitError = (),
|
|
||||||
> + 'static,
|
|
||||||
{
|
|
||||||
// create and configure default resource
|
|
||||||
self.default = Some(Rc::new(boxed::new_service(
|
|
||||||
f(Resource::new()).into_new_service().map_init_err(|_| ()),
|
|
||||||
)));
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register resource handler service.
|
|
||||||
pub fn service<F>(mut self, factory: F) -> Self
|
pub fn service<F>(mut self, factory: F) -> Self
|
||||||
where
|
where
|
||||||
F: HttpServiceFactory<P> + 'static,
|
F: HttpServiceFactory<P> + 'static,
|
||||||
{
|
{
|
||||||
let rdef = factory.rdef().clone();
|
self.services
|
||||||
|
.push(Box::new(ServiceFactoryWrapper::new(factory)));
|
||||||
self.services.push((
|
|
||||||
rdef,
|
|
||||||
boxed::new_service(factory.create().map_init_err(|_| ())),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -520,13 +347,34 @@ where
|
||||||
state: self.state,
|
state: self.state,
|
||||||
services: self.services,
|
services: self.services,
|
||||||
default: self.default,
|
default: self.default,
|
||||||
defaults: self.defaults,
|
|
||||||
factory_ref: self.factory_ref,
|
factory_ref: self.factory_ref,
|
||||||
extensions: self.extensions,
|
extensions: self.extensions,
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Default resource to be used if no matching route could be found.
|
||||||
|
///
|
||||||
|
/// Default resource works with resources only and does not work with
|
||||||
|
/// custom services.
|
||||||
|
pub fn default_resource<F, U>(mut self, f: F) -> Self
|
||||||
|
where
|
||||||
|
F: FnOnce(Resource<P>) -> Resource<P, U>,
|
||||||
|
U: NewService<
|
||||||
|
ServiceRequest<P>,
|
||||||
|
Response = ServiceResponse,
|
||||||
|
Error = (),
|
||||||
|
InitError = (),
|
||||||
|
> + 'static,
|
||||||
|
{
|
||||||
|
// create and configure default resource
|
||||||
|
self.default = Some(Rc::new(boxed::new_service(
|
||||||
|
f(Resource::new("")).into_new_service().map_init_err(|_| ()),
|
||||||
|
)));
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Register an external resource.
|
/// Register an external resource.
|
||||||
///
|
///
|
||||||
/// External resources are useful for URL generation purposes only
|
/// External resources are useful for URL generation purposes only
|
||||||
|
@ -583,19 +431,30 @@ where
|
||||||
{
|
{
|
||||||
fn into_new_service(self) -> AndThenNewService<AppInit<C, P>, T> {
|
fn into_new_service(self) -> AndThenNewService<AppInit<C, P>, T> {
|
||||||
// update resource default service
|
// update resource default service
|
||||||
if self.default.is_some() {
|
let default = self.default.unwrap_or_else(|| {
|
||||||
for default in &self.defaults {
|
Rc::new(boxed::new_service(fn_service(|req: ServiceRequest<P>| {
|
||||||
if default.borrow_mut().is_none() {
|
Ok(req.into_response(Response::NotFound().finish()))
|
||||||
*default.borrow_mut() = self.default.clone();
|
})))
|
||||||
}
|
});
|
||||||
}
|
|
||||||
}
|
let mut config = AppConfig::new(
|
||||||
|
"127.0.0.1:8080".parse().unwrap(),
|
||||||
|
"localhost:8080".to_owned(),
|
||||||
|
false,
|
||||||
|
default.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// register services
|
||||||
|
self.services
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|mut srv| srv.register(&mut config));
|
||||||
|
|
||||||
// set factory
|
// set factory
|
||||||
*self.factory_ref.borrow_mut() = Some(AppRoutingFactory {
|
*self.factory_ref.borrow_mut() = Some(AppRoutingFactory {
|
||||||
default: self.default.clone(),
|
default: default,
|
||||||
services: Rc::new(
|
services: Rc::new(
|
||||||
self.services
|
config
|
||||||
|
.into_services()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(rdef, srv, guards)| (rdef, srv, RefCell::new(guards)))
|
.map(|(rdef, srv, guards)| (rdef, srv, RefCell::new(guards)))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
@ -613,7 +472,7 @@ where
|
||||||
|
|
||||||
pub struct AppRoutingFactory<P> {
|
pub struct AppRoutingFactory<P> {
|
||||||
services: Rc<Vec<(ResourceDef, HttpNewService<P>, RefCell<Option<Guards>>)>>,
|
services: Rc<Vec<(ResourceDef, HttpNewService<P>, RefCell<Option<Guards>>)>>,
|
||||||
default: Option<Rc<HttpNewService<P>>>,
|
default: Rc<HttpNewService<P>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: 'static> NewService<ServiceRequest<P>> for AppRoutingFactory<P> {
|
impl<P: 'static> NewService<ServiceRequest<P>> for AppRoutingFactory<P> {
|
||||||
|
@ -624,12 +483,6 @@ impl<P: 'static> NewService<ServiceRequest<P>> for AppRoutingFactory<P> {
|
||||||
type Future = AppRoutingFactoryResponse<P>;
|
type Future = AppRoutingFactoryResponse<P>;
|
||||||
|
|
||||||
fn new_service(&self, _: &()) -> Self::Future {
|
fn new_service(&self, _: &()) -> Self::Future {
|
||||||
let default_fut = if let Some(ref default) = self.default {
|
|
||||||
Some(default.new_service(&()))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
AppRoutingFactoryResponse {
|
AppRoutingFactoryResponse {
|
||||||
fut: self
|
fut: self
|
||||||
.services
|
.services
|
||||||
|
@ -643,7 +496,7 @@ impl<P: 'static> NewService<ServiceRequest<P>> for AppRoutingFactory<P> {
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
default: None,
|
default: None,
|
||||||
default_fut,
|
default_fut: Some(self.default.new_service(&())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -929,16 +782,15 @@ where
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use actix_http::http::{Method, StatusCode};
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::test::{self, block_on, TestRequest};
|
use crate::http::{Method, StatusCode};
|
||||||
|
use crate::test::{self, block_on, init_service, TestRequest};
|
||||||
use crate::{web, HttpResponse, State};
|
use crate::{web, HttpResponse, State};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_default_resource() {
|
fn test_default_resource() {
|
||||||
let mut srv = test::init_service(
|
let mut srv = init_service(
|
||||||
App::new().resource("/test", |r| r.to(|| HttpResponse::Ok())),
|
App::new().service(web::resource("/test").to(|| HttpResponse::Ok())),
|
||||||
);
|
);
|
||||||
let req = TestRequest::with_uri("/test").to_request();
|
let req = TestRequest::with_uri("/test").to_request();
|
||||||
let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
|
@ -948,13 +800,14 @@ mod tests {
|
||||||
let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||||
|
|
||||||
let mut srv = test::init_service(
|
let mut srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.resource("/test", |r| r.to(|| HttpResponse::Ok()))
|
.service(web::resource("/test").to(|| HttpResponse::Ok()))
|
||||||
.resource("/test2", |r| {
|
.service(
|
||||||
r.default_resource(|r| r.to(|| HttpResponse::Created()))
|
web::resource("/test2")
|
||||||
.route(web::get().to(|| HttpResponse::Ok()))
|
.default_resource(|r| r.to(|| HttpResponse::Created()))
|
||||||
})
|
.route(web::get().to(|| HttpResponse::Ok())),
|
||||||
|
)
|
||||||
.default_resource(|r| r.to(|| HttpResponse::MethodNotAllowed())),
|
.default_resource(|r| r.to(|| HttpResponse::MethodNotAllowed())),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -975,22 +828,21 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_state() {
|
fn test_state() {
|
||||||
let mut srv = test::init_service(
|
let mut srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.state(10usize)
|
.state(10usize)
|
||||||
.resource("/", |r| r.to(|_: State<usize>| HttpResponse::Ok())),
|
.service(web::resource("/").to(|_: State<usize>| HttpResponse::Ok())),
|
||||||
);
|
);
|
||||||
|
|
||||||
let req = TestRequest::default().to_request();
|
let req = TestRequest::default().to_request();
|
||||||
let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
let mut srv = test::init_service(
|
let mut srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.state(10u32)
|
.state(10u32)
|
||||||
.resource("/", |r| r.to(|_: State<usize>| HttpResponse::Ok())),
|
.service(web::resource("/").to(|_: State<usize>| HttpResponse::Ok())),
|
||||||
);
|
);
|
||||||
|
|
||||||
let req = TestRequest::default().to_request();
|
let req = TestRequest::default().to_request();
|
||||||
let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||||
|
@ -998,22 +850,20 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_state_factory() {
|
fn test_state_factory() {
|
||||||
let mut srv = test::init_service(
|
let mut srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.state_factory(|| Ok::<_, ()>(10usize))
|
.state_factory(|| Ok::<_, ()>(10usize))
|
||||||
.resource("/", |r| r.to(|_: State<usize>| HttpResponse::Ok())),
|
.service(web::resource("/").to(|_: State<usize>| HttpResponse::Ok())),
|
||||||
);
|
);
|
||||||
|
|
||||||
let req = TestRequest::default().to_request();
|
let req = TestRequest::default().to_request();
|
||||||
let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
let mut srv = test::init_service(
|
let mut srv = init_service(
|
||||||
App::new()
|
App::new()
|
||||||
.state_factory(|| Ok::<_, ()>(10u32))
|
.state_factory(|| Ok::<_, ()>(10u32))
|
||||||
.resource("/", |r| r.to(|_: State<usize>| HttpResponse::Ok())),
|
.service(web::resource("/").to(|_: State<usize>| HttpResponse::Ok())),
|
||||||
);
|
);
|
||||||
|
|
||||||
let req = TestRequest::default().to_request();
|
let req = TestRequest::default().to_request();
|
||||||
let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||||
|
|
103
src/config.rs
Normal file
103
src/config.rs
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use actix_router::ResourceDef;
|
||||||
|
use actix_service::{boxed, IntoNewService, NewService};
|
||||||
|
|
||||||
|
use crate::guard::Guard;
|
||||||
|
use crate::service::{ServiceRequest, ServiceResponse};
|
||||||
|
|
||||||
|
type Guards = Vec<Box<Guard>>;
|
||||||
|
type HttpNewService<P> =
|
||||||
|
boxed::BoxedNewService<(), ServiceRequest<P>, ServiceResponse, (), ()>;
|
||||||
|
|
||||||
|
/// Application configuration
|
||||||
|
pub struct AppConfig<P> {
|
||||||
|
addr: SocketAddr,
|
||||||
|
secure: bool,
|
||||||
|
host: String,
|
||||||
|
root: bool,
|
||||||
|
default: Rc<HttpNewService<P>>,
|
||||||
|
services: Vec<(ResourceDef, HttpNewService<P>, Option<Guards>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: 'static> AppConfig<P> {
|
||||||
|
/// Crate server settings instance
|
||||||
|
pub(crate) fn new(
|
||||||
|
addr: SocketAddr,
|
||||||
|
host: String,
|
||||||
|
secure: bool,
|
||||||
|
default: Rc<HttpNewService<P>>,
|
||||||
|
) -> Self {
|
||||||
|
AppConfig {
|
||||||
|
addr,
|
||||||
|
secure,
|
||||||
|
host,
|
||||||
|
default,
|
||||||
|
root: true,
|
||||||
|
services: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if root is beeing configured
|
||||||
|
pub fn is_root(&self) -> bool {
|
||||||
|
self.root
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn into_services(
|
||||||
|
self,
|
||||||
|
) -> Vec<(ResourceDef, HttpNewService<P>, Option<Guards>)> {
|
||||||
|
self.services
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn clone_config(&self) -> Self {
|
||||||
|
AppConfig {
|
||||||
|
addr: self.addr,
|
||||||
|
secure: self.secure,
|
||||||
|
host: self.host.clone(),
|
||||||
|
default: self.default.clone(),
|
||||||
|
services: Vec::new(),
|
||||||
|
root: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the socket address of the local half of this TCP connection
|
||||||
|
pub fn local_addr(&self) -> SocketAddr {
|
||||||
|
self.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if connection is secure(https)
|
||||||
|
pub fn secure(&self) -> bool {
|
||||||
|
self.secure
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns host header value
|
||||||
|
pub fn host(&self) -> &str {
|
||||||
|
&self.host
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn default_service(&self) -> Rc<HttpNewService<P>> {
|
||||||
|
self.default.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_service<F, S>(
|
||||||
|
&mut self,
|
||||||
|
rdef: ResourceDef,
|
||||||
|
guards: Option<Vec<Box<Guard>>>,
|
||||||
|
service: F,
|
||||||
|
) where
|
||||||
|
F: IntoNewService<S, ServiceRequest<P>>,
|
||||||
|
S: NewService<
|
||||||
|
ServiceRequest<P>,
|
||||||
|
Response = ServiceResponse,
|
||||||
|
Error = (),
|
||||||
|
InitError = (),
|
||||||
|
> + 'static,
|
||||||
|
{
|
||||||
|
self.services.push((
|
||||||
|
rdef,
|
||||||
|
boxed::new_service(service.into_new_service()),
|
||||||
|
guards,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
104
src/extract.rs
104
src/extract.rs
|
@ -88,9 +88,9 @@ impl ExtractorConfig for () {
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().resource(
|
/// let app = App::new().service(
|
||||||
/// "/{username}/{count}/index.html", // <- define path parameters
|
/// web::resource("/{username}/{count}/index.html") // <- define path parameters
|
||||||
/// |r| r.route(web::get().to(index)) // <- register handler with `Path` extractor
|
/// .route(web::get().to(index)) // <- register handler with `Path` extractor
|
||||||
/// );
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -113,9 +113,9 @@ impl ExtractorConfig for () {
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().resource(
|
/// let app = App::new().service(
|
||||||
/// "/{username}/index.html", // <- define path parameters
|
/// web::resource("/{username}/index.html") // <- define path parameters
|
||||||
/// |r| r.route(web::get().to(index)) // <- use handler with Path` extractor
|
/// .route(web::get().to(index)) // <- use handler with Path` extractor
|
||||||
/// );
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -180,9 +180,9 @@ impl<T> From<T> for Path<T> {
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().resource(
|
/// let app = App::new().service(
|
||||||
/// "/{username}/{count}/index.html", // <- define path parameters
|
/// web::resource("/{username}/{count}/index.html") // <- define path parameters
|
||||||
/// |r| r.route(web::get().to(index)) // <- register handler with `Path` extractor
|
/// .route(web::get().to(index)) // <- register handler with `Path` extractor
|
||||||
/// );
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -205,9 +205,9 @@ impl<T> From<T> for Path<T> {
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().resource(
|
/// let app = App::new().service(
|
||||||
/// "/{username}/index.html", // <- define path parameters
|
/// web::resource("/{username}/index.html") // <- define path parameters
|
||||||
/// |r| r.route(web::get().to(index)) // <- use handler with Path` extractor
|
/// .route(web::get().to(index)) // <- use handler with Path` extractor
|
||||||
/// );
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -266,9 +266,8 @@ impl<T: fmt::Display> fmt::Display for Path<T> {
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().resource(
|
/// let app = App::new().service(
|
||||||
/// "/index.html",
|
/// web::resource("/index.html").route(web::get().to(index))); // <- use `Query` extractor
|
||||||
/// |r| r.route(web::get().to(index))); // <- use `Query` extractor
|
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Query<T>(T);
|
pub struct Query<T>(T);
|
||||||
|
@ -322,9 +321,9 @@ impl<T> Query<T> {
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().resource(
|
/// let app = App::new().service(
|
||||||
/// "/index.html",
|
/// web::resource("/index.html")
|
||||||
/// |r| r.route(web::get().to(index))); // <- use `Query` extractor
|
/// .route(web::get().to(index))); // <- use `Query` extractor
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
impl<T, P> FromRequest<P> for Query<T>
|
impl<T, P> FromRequest<P> for Query<T>
|
||||||
|
@ -462,14 +461,13 @@ impl<T: fmt::Display> fmt::Display for Form<T> {
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().resource(
|
/// let app = App::new().service(
|
||||||
/// "/index.html",
|
/// web::resource("/index.html")
|
||||||
/// |r| {
|
/// .route(web::get()
|
||||||
/// r.route(web::get()
|
|
||||||
/// // change `Form` extractor configuration
|
/// // change `Form` extractor configuration
|
||||||
/// .config(extract::FormConfig::default().limit(4097))
|
/// .config(extract::FormConfig::default().limit(4097))
|
||||||
/// .to(index))
|
/// .to(index))
|
||||||
/// });
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -535,9 +533,10 @@ impl Default for FormConfig {
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().resource(
|
/// let app = App::new().service(
|
||||||
/// "/index.html",
|
/// web::resource("/index.html").route(
|
||||||
/// |r| r.route(web::post().to(index)));
|
/// web::post().to(index))
|
||||||
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
@ -645,9 +644,10 @@ impl<T: Serialize> Responder for Json<T> {
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().resource(
|
/// let app = App::new().service(
|
||||||
/// "/index.html",
|
/// web::resource("/index.html").route(
|
||||||
/// |r| r.route(web::post().to(index)));
|
/// web::post().to(index))
|
||||||
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
impl<T, P> FromRequest<P> for Json<T>
|
impl<T, P> FromRequest<P> for Json<T>
|
||||||
|
@ -692,16 +692,17 @@ where
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().resource("/index.html", |r| {
|
/// let app = App::new().service(
|
||||||
/// r.route(web::post().config(
|
/// web::resource("/index.html").route(
|
||||||
/// // change json extractor configuration
|
/// web::post().config(
|
||||||
/// extract::JsonConfig::default().limit(4096)
|
/// // change json extractor configuration
|
||||||
/// .error_handler(|err, req| { // <- create custom error response
|
/// extract::JsonConfig::default().limit(4096)
|
||||||
/// error::InternalError::from_response(
|
/// .error_handler(|err, req| { // <- create custom error response
|
||||||
/// err, HttpResponse::Conflict().finish()).into()
|
/// error::InternalError::from_response(
|
||||||
/// }))
|
/// err, HttpResponse::Conflict().finish()).into()
|
||||||
/// .to(index))
|
/// }))
|
||||||
/// });
|
/// .to(index))
|
||||||
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -757,8 +758,10 @@ impl Default for JsonConfig {
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new()
|
/// let app = App::new().service(
|
||||||
/// .resource("/index.html", |r| r.route(web::get().to(index)));
|
/// web::resource("/index.html").route(
|
||||||
|
/// web::get().to(index))
|
||||||
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
impl<P> FromRequest<P> for Bytes
|
impl<P> FromRequest<P> for Bytes
|
||||||
|
@ -801,12 +804,12 @@ where
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().resource("/index.html", |r| {
|
/// let app = App::new().service(
|
||||||
/// r.route(
|
/// web::resource("/index.html").route(
|
||||||
/// web::get()
|
/// web::get()
|
||||||
/// .config(extract::PayloadConfig::new(4096)) // <- limit size of the payload
|
/// .config(extract::PayloadConfig::new(4096)) // <- limit size of the payload
|
||||||
/// .to(index)) // <- register handler with extractor params
|
/// .to(index)) // <- register handler with extractor params
|
||||||
/// });
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
impl<P> FromRequest<P> for String
|
impl<P> FromRequest<P> for String
|
||||||
|
@ -896,9 +899,10 @@ where
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().resource("/users/:first", |r| {
|
/// let app = App::new().service(
|
||||||
/// r.route(web::post().to(index))
|
/// web::resource("/users/:first").route(
|
||||||
/// });
|
/// web::post().to(index))
|
||||||
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
impl<T: 'static, P> FromRequest<P> for Option<T>
|
impl<T: 'static, P> FromRequest<P> for Option<T>
|
||||||
|
@ -959,9 +963,9 @@ where
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().resource("/users/:first", |r| {
|
/// let app = App::new().service(
|
||||||
/// r.route(web::post().to(index))
|
/// web::resource("/users/:first").route(web::post().to(index))
|
||||||
/// });
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
impl<T: 'static, P> FromRequest<P> for Result<T, T::Error>
|
impl<T: 'static, P> FromRequest<P> for Result<T, T::Error>
|
||||||
|
|
15
src/guard.rs
15
src/guard.rs
|
@ -19,11 +19,10 @@ pub trait Guard {
|
||||||
/// use actix_web::{web, guard, App, HttpResponse};
|
/// use actix_web::{web, guard, App, HttpResponse};
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// App::new().resource("/index.html", |r|
|
/// App::new().service(web::resource("/index.html").route(
|
||||||
/// r.route(
|
/// web::route()
|
||||||
/// web::route()
|
/// .guard(guard::Any(guard::Get()).or(guard::Post()))
|
||||||
/// .guard(guard::Any(guard::Get()).or(guard::Post()))
|
/// .to(|| HttpResponse::MethodNotAllowed()))
|
||||||
/// .to(|| HttpResponse::MethodNotAllowed()))
|
|
||||||
/// );
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -60,12 +59,12 @@ impl Guard for AnyGuard {
|
||||||
/// use actix_web::{guard, web, App, HttpResponse};
|
/// use actix_web::{guard, web, App, HttpResponse};
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// App::new().resource("/index.html", |r| {
|
/// App::new().service(web::resource("/index.html").route(
|
||||||
/// r.route(web::route()
|
/// web::route()
|
||||||
/// .guard(
|
/// .guard(
|
||||||
/// guard::All(guard::Get()).and(guard::Header("content-type", "text/plain")))
|
/// guard::All(guard::Get()).and(guard::Header("content-type", "text/plain")))
|
||||||
/// .to(|| HttpResponse::MethodNotAllowed()))
|
/// .to(|| HttpResponse::MethodNotAllowed()))
|
||||||
/// });
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn All<F: Guard + 'static>(guard: F) -> AllGuard {
|
pub fn All<F: Guard + 'static>(guard: F) -> AllGuard {
|
||||||
|
|
91
src/lib.rs
91
src/lib.rs
|
@ -5,6 +5,7 @@ pub mod extract;
|
||||||
mod handler;
|
mod handler;
|
||||||
// mod info;
|
// mod info;
|
||||||
pub mod blocking;
|
pub mod blocking;
|
||||||
|
mod config;
|
||||||
pub mod guard;
|
pub mod guard;
|
||||||
pub mod middleware;
|
pub mod middleware;
|
||||||
mod request;
|
mod request;
|
||||||
|
@ -43,13 +44,24 @@ pub mod dev {
|
||||||
//! use actix_web::dev::*;
|
//! use actix_web::dev::*;
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
pub use crate::app::{AppRouter, HttpServiceFactory};
|
pub use crate::app::AppRouter;
|
||||||
|
pub use crate::config::AppConfig;
|
||||||
|
pub use crate::service::HttpServiceFactory;
|
||||||
|
|
||||||
pub use actix_http::body::{Body, MessageBody, ResponseBody};
|
pub use actix_http::body::{Body, MessageBody, ResponseBody};
|
||||||
pub use actix_http::dev::ResponseBuilder as HttpResponseBuilder;
|
pub use actix_http::dev::ResponseBuilder as HttpResponseBuilder;
|
||||||
pub use actix_http::{
|
pub use actix_http::{
|
||||||
Extensions, Payload, PayloadStream, RequestHead, ResponseHead,
|
Extensions, Payload, PayloadStream, RequestHead, ResponseHead,
|
||||||
};
|
};
|
||||||
pub use actix_router::{Path, ResourceDef, Url};
|
pub use actix_router::{Path, ResourceDef, Url};
|
||||||
|
|
||||||
|
pub(crate) fn insert_slash(path: &str) -> String {
|
||||||
|
let mut path = path.to_owned();
|
||||||
|
if !path.is_empty() && !path.starts_with('/') {
|
||||||
|
path.insert(0, '/');
|
||||||
|
};
|
||||||
|
path
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod web {
|
pub mod web {
|
||||||
|
@ -58,8 +70,74 @@ pub mod web {
|
||||||
|
|
||||||
use crate::extract::FromRequest;
|
use crate::extract::FromRequest;
|
||||||
use crate::handler::{AsyncFactory, Factory};
|
use crate::handler::{AsyncFactory, Factory};
|
||||||
|
use crate::resource::Resource;
|
||||||
use crate::responder::Responder;
|
use crate::responder::Responder;
|
||||||
use crate::Route;
|
use crate::route::Route;
|
||||||
|
use crate::scope::Scope;
|
||||||
|
|
||||||
|
/// Create resource for a specific path.
|
||||||
|
///
|
||||||
|
/// Resources may have variable path segments. For example, a
|
||||||
|
/// resource with the path `/a/{name}/c` would match all incoming
|
||||||
|
/// requests with paths such as `/a/b/c`, `/a/1/c`, or `/a/etc/c`.
|
||||||
|
///
|
||||||
|
/// A variable segment is specified in the form `{identifier}`,
|
||||||
|
/// where the identifier can be used later in a request handler to
|
||||||
|
/// access the matched value for that segment. This is done by
|
||||||
|
/// looking up the identifier in the `Params` object returned by
|
||||||
|
/// `HttpRequest.match_info()` method.
|
||||||
|
///
|
||||||
|
/// By default, each segment matches the regular expression `[^{}/]+`.
|
||||||
|
///
|
||||||
|
/// You can also specify a custom regex in the form `{identifier:regex}`:
|
||||||
|
///
|
||||||
|
/// For instance, to route `GET`-requests on any route matching
|
||||||
|
/// `/users/{userid}/{friend}` and store `userid` and `friend` in
|
||||||
|
/// the exposed `Params` object:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # extern crate actix_web;
|
||||||
|
/// use actix_web::{web, http, App, HttpResponse};
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let app = App::new().service(
|
||||||
|
/// web::resource("/users/{userid}/{friend}")
|
||||||
|
/// .route(web::get().to(|| HttpResponse::Ok()))
|
||||||
|
/// .route(web::head().to(|| HttpResponse::MethodNotAllowed()))
|
||||||
|
/// );
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn resource<P: 'static>(path: &str) -> Resource<P> {
|
||||||
|
Resource::new(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure scope for common root path.
|
||||||
|
///
|
||||||
|
/// Scopes collect multiple paths under a common path prefix.
|
||||||
|
/// Scope path can contain variable path segments as resources.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # extern crate actix_web;
|
||||||
|
/// use actix_web::{web, App, HttpRequest, HttpResponse};
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let app = App::new().service(
|
||||||
|
/// web::scope("/{project_id}")
|
||||||
|
/// .service(web::resource("/path1").to(|| HttpResponse::Ok()))
|
||||||
|
/// .service(web::resource("/path2").to(|| HttpResponse::Ok()))
|
||||||
|
/// .service(web::resource("/path3").to(|| HttpResponse::MethodNotAllowed()))
|
||||||
|
/// );
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// In the above example, three routes get added:
|
||||||
|
/// * /{project_id}/path1
|
||||||
|
/// * /{project_id}/path2
|
||||||
|
/// * /{project_id}/path3
|
||||||
|
///
|
||||||
|
pub fn scope<P: 'static>(path: &str) -> Scope<P> {
|
||||||
|
Scope::new(path)
|
||||||
|
}
|
||||||
|
|
||||||
/// Create **route** without configuration.
|
/// Create **route** without configuration.
|
||||||
pub fn route<P: 'static>() -> Route<P> {
|
pub fn route<P: 'static>() -> Route<P> {
|
||||||
|
@ -105,7 +183,10 @@ pub mod web {
|
||||||
/// unimplemented!()
|
/// unimplemented!()
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// App::new().resource("/", |r| r.route(web::to(index)));
|
/// App::new().service(
|
||||||
|
/// web::resource("/").route(
|
||||||
|
/// web::to(index))
|
||||||
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
pub fn to<F, I, R, P: 'static>(handler: F) -> Route<P>
|
pub fn to<F, I, R, P: 'static>(handler: F) -> Route<P>
|
||||||
where
|
where
|
||||||
|
@ -125,7 +206,9 @@ pub mod web {
|
||||||
/// futures::future::ok(HttpResponse::Ok().finish())
|
/// futures::future::ok(HttpResponse::Ok().finish())
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// App::new().resource("/", |r| r.route(web::to_async(index)));
|
/// App::new().service(web::resource("/").route(
|
||||||
|
/// web::to_async(index))
|
||||||
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
pub fn to_async<F, I, R, P: 'static>(handler: F) -> Route<P>
|
pub fn to_async<F, I, R, P: 'static>(handler: F) -> Route<P>
|
||||||
where
|
where
|
||||||
|
|
|
@ -19,10 +19,11 @@ use crate::service::{ServiceRequest, ServiceResponse};
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new()
|
/// let app = App::new()
|
||||||
/// .middleware(middleware::DefaultHeaders::new().header("X-Version", "0.2"))
|
/// .middleware(middleware::DefaultHeaders::new().header("X-Version", "0.2"))
|
||||||
/// .resource("/test", |r| {
|
/// .service(
|
||||||
/// r.route(web::get().to(|| HttpResponse::Ok()))
|
/// web::resource("/test")
|
||||||
/// .route(web::method(http::Method::HEAD).to(|| HttpResponse::MethodNotAllowed()))
|
/// .route(web::get().to(|| HttpResponse::Ok()))
|
||||||
/// });
|
/// .route(web::method(http::Method::HEAD).to(|| HttpResponse::MethodNotAllowed()))
|
||||||
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
|
@ -149,9 +149,10 @@ impl HttpMessage for HttpRequest {
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().resource("/users/:first", |r| {
|
/// let app = App::new().service(
|
||||||
/// r.route(web::get().to(index))
|
/// web::resource("/users/{first}").route(
|
||||||
/// });
|
/// web::get().to(index))
|
||||||
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
impl<P> FromRequest<P> for HttpRequest {
|
impl<P> FromRequest<P> for HttpRequest {
|
||||||
|
|
117
src/resource.rs
117
src/resource.rs
|
@ -9,7 +9,9 @@ use actix_service::{
|
||||||
use futures::future::{ok, Either, FutureResult};
|
use futures::future::{ok, Either, FutureResult};
|
||||||
use futures::{Async, Future, IntoFuture, Poll};
|
use futures::{Async, Future, IntoFuture, Poll};
|
||||||
|
|
||||||
|
use crate::dev::{insert_slash, AppConfig, HttpServiceFactory, ResourceDef};
|
||||||
use crate::extract::FromRequest;
|
use crate::extract::FromRequest;
|
||||||
|
use crate::guard::Guard;
|
||||||
use crate::handler::{AsyncFactory, Factory};
|
use crate::handler::{AsyncFactory, Factory};
|
||||||
use crate::responder::Responder;
|
use crate::responder::Responder;
|
||||||
use crate::route::{CreateRouteService, Route, RouteService};
|
use crate::route::{CreateRouteService, Route, RouteService};
|
||||||
|
@ -32,38 +34,37 @@ type HttpNewService<P> = BoxedNewService<(), ServiceRequest<P>, ServiceResponse,
|
||||||
/// use actix_web::{web, App, HttpResponse};
|
/// use actix_web::{web, App, HttpResponse};
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new()
|
/// let app = App::new().service(
|
||||||
/// .resource(
|
/// web::resource("/")
|
||||||
/// "/", |r| r.route(web::get().to(|| HttpResponse::Ok())));
|
/// .route(web::get().to(|| HttpResponse::Ok())));
|
||||||
/// }
|
/// }
|
||||||
pub struct Resource<P, T = ResourceEndpoint<P>> {
|
pub struct Resource<P, T = ResourceEndpoint<P>> {
|
||||||
routes: Vec<Route<P>>,
|
|
||||||
endpoint: T,
|
endpoint: T,
|
||||||
|
rdef: String,
|
||||||
|
routes: Vec<Route<P>>,
|
||||||
|
guards: Vec<Box<Guard>>,
|
||||||
default: Rc<RefCell<Option<Rc<HttpNewService<P>>>>>,
|
default: Rc<RefCell<Option<Rc<HttpNewService<P>>>>>,
|
||||||
factory_ref: Rc<RefCell<Option<ResourceFactory<P>>>>,
|
factory_ref: Rc<RefCell<Option<ResourceFactory<P>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P> Resource<P> {
|
impl<P> Resource<P> {
|
||||||
pub fn new() -> Resource<P> {
|
pub fn new(path: &str) -> Resource<P> {
|
||||||
let fref = Rc::new(RefCell::new(None));
|
let fref = Rc::new(RefCell::new(None));
|
||||||
|
|
||||||
Resource {
|
Resource {
|
||||||
routes: Vec::new(),
|
routes: Vec::new(),
|
||||||
|
rdef: path.to_string(),
|
||||||
endpoint: ResourceEndpoint::new(fref.clone()),
|
endpoint: ResourceEndpoint::new(fref.clone()),
|
||||||
factory_ref: fref,
|
factory_ref: fref,
|
||||||
|
guards: Vec::new(),
|
||||||
default: Rc::new(RefCell::new(None)),
|
default: Rc::new(RefCell::new(None)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P> Default for Resource<P> {
|
impl<P, T> Resource<P, T>
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: 'static, T> Resource<P, T>
|
|
||||||
where
|
where
|
||||||
|
P: 'static,
|
||||||
T: NewService<
|
T: NewService<
|
||||||
ServiceRequest<P>,
|
ServiceRequest<P>,
|
||||||
Response = ServiceResponse,
|
Response = ServiceResponse,
|
||||||
|
@ -71,19 +72,52 @@ where
|
||||||
InitError = (),
|
InitError = (),
|
||||||
>,
|
>,
|
||||||
{
|
{
|
||||||
|
/// Add match guard to a resource.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use actix_web::{web, guard, App, HttpResponse, extract::Path};
|
||||||
|
///
|
||||||
|
/// fn index(data: 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<G: Guard + 'static>(mut self, guard: G) -> Self {
|
||||||
|
self.guards.push(Box::new(guard));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_guards(mut self, guards: Vec<Box<Guard>>) -> Self {
|
||||||
|
self.guards.extend(guards);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Register a new route.
|
/// Register a new route.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use actix_web::{web, guard, App, HttpResponse};
|
/// use actix_web::{web, guard, App, HttpResponse};
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new()
|
/// let app = App::new().service(
|
||||||
/// .resource("/", |r| {
|
/// web::resource("/").route(
|
||||||
/// r.route(web::route()
|
/// web::route()
|
||||||
/// .guard(guard::Any(guard::Get()).or(guard::Put()))
|
/// .guard(guard::Any(guard::Get()).or(guard::Put()))
|
||||||
/// .guard(guard::Header("Content-Type", "text/plain"))
|
/// .guard(guard::Header("Content-Type", "text/plain"))
|
||||||
/// .to(|| HttpResponse::Ok()))
|
/// .to(|| HttpResponse::Ok()))
|
||||||
/// });
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
@ -93,12 +127,12 @@ where
|
||||||
/// use actix_web::{web, guard, App, HttpResponse};
|
/// use actix_web::{web, guard, App, HttpResponse};
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new()
|
/// let app = App::new().service(
|
||||||
/// .resource("/container/", |r| {
|
/// web::resource("/container/")
|
||||||
/// r.route(web::get().to(get_handler))
|
/// .route(web::get().to(get_handler))
|
||||||
/// .route(web::post().to(post_handler))
|
/// .route(web::post().to(post_handler))
|
||||||
/// .route(web::delete().to(delete_handler))
|
/// .route(web::delete().to(delete_handler))
|
||||||
/// });
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// # fn get_handler() {}
|
/// # fn get_handler() {}
|
||||||
/// # fn post_handler() {}
|
/// # fn post_handler() {}
|
||||||
|
@ -109,8 +143,7 @@ where
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register a new route and add handler. This route get called for all
|
/// Register a new route and add handler. This route matches all requests.
|
||||||
/// requests.
|
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use actix_web::*;
|
/// use actix_web::*;
|
||||||
|
@ -119,7 +152,7 @@ where
|
||||||
/// unimplemented!()
|
/// unimplemented!()
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// App::new().resource("/", |r| r.to(index));
|
/// App::new().service(web::resource("/").to(index));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// This is shortcut for:
|
/// This is shortcut for:
|
||||||
|
@ -128,7 +161,7 @@ where
|
||||||
/// # extern crate actix_web;
|
/// # extern crate actix_web;
|
||||||
/// # use actix_web::*;
|
/// # use actix_web::*;
|
||||||
/// # fn index(req: HttpRequest) -> HttpResponse { unimplemented!() }
|
/// # fn index(req: HttpRequest) -> HttpResponse { unimplemented!() }
|
||||||
/// App::new().resource("/", |r| r.route(web::route().to(index)));
|
/// App::new().service(web::resource("/").route(web::route().to(index)));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn to<F, I, R>(mut self, handler: F) -> Self
|
pub fn to<F, I, R>(mut self, handler: F) -> Self
|
||||||
where
|
where
|
||||||
|
@ -150,7 +183,7 @@ where
|
||||||
/// ok(HttpResponse::Ok().finish())
|
/// ok(HttpResponse::Ok().finish())
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// App::new().resource("/", |r| r.to_async(index));
|
/// App::new().service(web::resource("/").to_async(index));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// This is shortcut for:
|
/// This is shortcut for:
|
||||||
|
@ -161,7 +194,7 @@ where
|
||||||
/// # fn index(req: HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
|
/// # fn index(req: HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
|
||||||
/// # unimplemented!()
|
/// # unimplemented!()
|
||||||
/// # }
|
/// # }
|
||||||
/// App::new().resource("/", |r| r.route(web::route().to_async(index)));
|
/// App::new().service(web::resource("/").route(web::route().to_async(index)));
|
||||||
/// ```
|
/// ```
|
||||||
#[allow(clippy::wrong_self_convention)]
|
#[allow(clippy::wrong_self_convention)]
|
||||||
pub fn to_async<F, I, R>(mut self, handler: F) -> Self
|
pub fn to_async<F, I, R>(mut self, handler: F) -> Self
|
||||||
|
@ -206,6 +239,8 @@ where
|
||||||
let endpoint = ApplyTransform::new(mw, self.endpoint);
|
let endpoint = ApplyTransform::new(mw, self.endpoint);
|
||||||
Resource {
|
Resource {
|
||||||
endpoint,
|
endpoint,
|
||||||
|
rdef: self.rdef,
|
||||||
|
guards: self.guards,
|
||||||
routes: self.routes,
|
routes: self.routes,
|
||||||
default: self.default,
|
default: self.default,
|
||||||
factory_ref: self.factory_ref,
|
factory_ref: self.factory_ref,
|
||||||
|
@ -222,14 +257,38 @@ where
|
||||||
{
|
{
|
||||||
// create and configure default resource
|
// create and configure default resource
|
||||||
self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::new_service(
|
self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::new_service(
|
||||||
f(Resource::new()).into_new_service().map_init_err(|_| ()),
|
f(Resource::new("")).into_new_service().map_init_err(|_| ()),
|
||||||
)))));
|
)))));
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn get_default(&self) -> Rc<RefCell<Option<Rc<HttpNewService<P>>>>> {
|
impl<P, T> HttpServiceFactory<P> for Resource<P, T>
|
||||||
self.default.clone()
|
where
|
||||||
|
P: 'static,
|
||||||
|
T: NewService<
|
||||||
|
ServiceRequest<P>,
|
||||||
|
Response = ServiceResponse,
|
||||||
|
Error = (),
|
||||||
|
InitError = (),
|
||||||
|
> + 'static,
|
||||||
|
{
|
||||||
|
fn register(mut self, config: &mut AppConfig<P>) {
|
||||||
|
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() {
|
||||||
|
ResourceDef::new(&insert_slash(&self.rdef))
|
||||||
|
} else {
|
||||||
|
ResourceDef::new(&insert_slash(&self.rdef))
|
||||||
|
};
|
||||||
|
config.register_service(rdef, guards, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -288,22 +288,21 @@ where
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
// use actix_http::body::Body;
|
use actix_service::Service;
|
||||||
use actix_http::body::{Body, ResponseBody};
|
|
||||||
use actix_http::http::StatusCode;
|
|
||||||
use actix_service::{IntoNewService, NewService, Service};
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
|
||||||
use crate::test::TestRequest;
|
use crate::body::{Body, ResponseBody};
|
||||||
use crate::App;
|
use crate::http::StatusCode;
|
||||||
|
use crate::test::{init_service, TestRequest};
|
||||||
|
use crate::{web, App};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_option_responder() {
|
fn test_option_responder() {
|
||||||
let app = App::new()
|
let mut srv = init_service(
|
||||||
.resource("/none", |r| r.to(|| -> Option<&'static str> { None }))
|
App::new()
|
||||||
.resource("/some", |r| r.to(|| Some("some")))
|
.service(web::resource("/none").to(|| -> Option<&'static str> { None }))
|
||||||
.into_new_service();
|
.service(web::resource("/some").to(|| Some("some"))),
|
||||||
let mut srv = TestRequest::block_on(app.new_service(&())).unwrap();
|
);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/none").to_request();
|
let req = TestRequest::with_uri("/none").to_request();
|
||||||
let resp = TestRequest::block_on(srv.call(req)).unwrap();
|
let resp = TestRequest::block_on(srv.call(req)).unwrap();
|
||||||
|
|
43
src/route.rs
43
src/route.rs
|
@ -84,6 +84,10 @@ impl<P: 'static> Route<P> {
|
||||||
*self.config_ref.borrow_mut() = self.config.storage.clone();
|
*self.config_ref.borrow_mut() = self.config.storage.clone();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn take_guards(&mut self) -> Vec<Box<Guard>> {
|
||||||
|
std::mem::replace(Rc::get_mut(&mut self.guards).unwrap(), Vec::new())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P> NewService<ServiceRequest<P>> for Route<P> {
|
impl<P> NewService<ServiceRequest<P>> for Route<P> {
|
||||||
|
@ -161,12 +165,12 @@ impl<P: 'static> Route<P> {
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use actix_web::*;
|
/// # use actix_web::*;
|
||||||
/// # fn main() {
|
/// # fn main() {
|
||||||
/// App::new().resource("/path", |r| {
|
/// App::new().service(web::resource("/path").route(
|
||||||
/// r.route(web::get()
|
/// web::get()
|
||||||
/// .guard(guard::Get())
|
/// .method(http::Method::CONNECT)
|
||||||
/// .guard(guard::Header("content-type", "text/plain"))
|
/// .guard(guard::Header("content-type", "text/plain"))
|
||||||
/// .to(|req: HttpRequest| HttpResponse::Ok()))
|
/// .to(|req: HttpRequest| HttpResponse::Ok()))
|
||||||
/// });
|
/// );
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn method(mut self, method: Method) -> Self {
|
pub fn method(mut self, method: Method) -> Self {
|
||||||
|
@ -181,12 +185,12 @@ impl<P: 'static> Route<P> {
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use actix_web::*;
|
/// # use actix_web::*;
|
||||||
/// # fn main() {
|
/// # fn main() {
|
||||||
/// App::new().resource("/path", |r| {
|
/// App::new().service(web::resource("/path").route(
|
||||||
/// r.route(web::route()
|
/// web::route()
|
||||||
/// .guard(guard::Get())
|
/// .guard(guard::Get())
|
||||||
/// .guard(guard::Header("content-type", "text/plain"))
|
/// .guard(guard::Header("content-type", "text/plain"))
|
||||||
/// .to(|req: HttpRequest| HttpResponse::Ok()))
|
/// .to(|req: HttpRequest| HttpResponse::Ok()))
|
||||||
/// });
|
/// );
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn guard<F: Guard + 'static>(mut self, f: F) -> Self {
|
pub fn guard<F: Guard + 'static>(mut self, f: F) -> Self {
|
||||||
|
@ -229,9 +233,9 @@ impl<P: 'static> Route<P> {
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().resource(
|
/// let app = App::new().service(
|
||||||
/// "/{username}/index.html", // <- define path parameters
|
/// web::resource("/{username}/index.html") // <- define path parameters
|
||||||
/// |r| r.route(web::get().to(index)), // <- register handler
|
/// .route(web::get().to(index)) // <- register handler
|
||||||
/// );
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -254,9 +258,9 @@ impl<P: 'static> Route<P> {
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().resource(
|
/// let app = App::new().service(
|
||||||
/// "/{username}/index.html", // <- define path parameters
|
/// web::resource("/{username}/index.html") // <- define path parameters
|
||||||
/// |r| r.route(web::get().to(index)),
|
/// .route(web::get().to(index))
|
||||||
/// );
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -294,9 +298,9 @@ impl<P: 'static> Route<P> {
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().resource(
|
/// let app = App::new().service(
|
||||||
/// "/{username}/index.html", // <- define path parameters
|
/// web::resource("/{username}/index.html") // <- define path parameters
|
||||||
/// |r| r.route(web::get().to_async(index)), // <- register async handler
|
/// .route(web::get().to_async(index)) // <- register async handler
|
||||||
/// );
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -328,15 +332,14 @@ impl<P: 'static> Route<P> {
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().resource("/index.html", |r| {
|
/// let app = App::new().service(
|
||||||
/// r.route(
|
/// web::resource("/index.html").route(
|
||||||
/// web::get()
|
/// web::get()
|
||||||
/// // limit size of the payload
|
/// // limit size of the payload
|
||||||
/// .config(extract::PayloadConfig::new(4096))
|
/// .config(extract::PayloadConfig::new(4096))
|
||||||
/// // register handler
|
/// // register handler
|
||||||
/// .to(index)
|
/// .to(index)
|
||||||
/// )
|
/// ));
|
||||||
/// });
|
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn config<C: ExtractorConfig>(mut self, config: C) -> Self {
|
pub fn config<C: ExtractorConfig>(mut self, config: C) -> Self {
|
||||||
|
|
456
src/scope.rs
456
src/scope.rs
|
@ -10,10 +10,13 @@ use actix_service::{
|
||||||
use futures::future::{ok, Either, Future, FutureResult};
|
use futures::future::{ok, Either, Future, FutureResult};
|
||||||
use futures::{Async, Poll};
|
use futures::{Async, Poll};
|
||||||
|
|
||||||
|
use crate::dev::{insert_slash, AppConfig, HttpServiceFactory};
|
||||||
use crate::guard::Guard;
|
use crate::guard::Guard;
|
||||||
use crate::resource::Resource;
|
use crate::resource::Resource;
|
||||||
use crate::route::Route;
|
use crate::route::Route;
|
||||||
use crate::service::{ServiceRequest, ServiceResponse};
|
use crate::service::{
|
||||||
|
ServiceFactory, ServiceFactoryWrapper, ServiceRequest, ServiceResponse,
|
||||||
|
};
|
||||||
|
|
||||||
type Guards = Vec<Box<Guard>>;
|
type Guards = Vec<Box<Guard>>;
|
||||||
type HttpService<P> = BoxedService<ServiceRequest<P>, ServiceResponse, ()>;
|
type HttpService<P> = BoxedService<ServiceRequest<P>, ServiceResponse, ()>;
|
||||||
|
@ -35,12 +38,12 @@ type BoxedResponse = Box<Future<Item = ServiceResponse, Error = ()>>;
|
||||||
/// use actix_web::{web, App, HttpResponse};
|
/// use actix_web::{web, App, HttpResponse};
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().scope("/{project_id}/", |scope| {
|
/// let app = App::new().service(
|
||||||
/// scope
|
/// web::scope("/{project_id}/")
|
||||||
/// .resource("/path1", |r| r.to(|| HttpResponse::Ok()))
|
/// .service(web::resource("/path1").to(|| HttpResponse::Ok()))
|
||||||
/// .resource("/path2", |r| r.route(web::get().to(|| HttpResponse::Ok())))
|
/// .service(web::resource("/path2").route(web::get().to(|| HttpResponse::Ok())))
|
||||||
/// .resource("/path3", |r| r.route(web::head().to(|| HttpResponse::MethodNotAllowed())))
|
/// .service(web::resource("/path3").route(web::head().to(|| HttpResponse::MethodNotAllowed())))
|
||||||
/// });
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
@ -51,11 +54,10 @@ type BoxedResponse = Box<Future<Item = ServiceResponse, Error = ()>>;
|
||||||
///
|
///
|
||||||
pub struct Scope<P, T = ScopeEndpoint<P>> {
|
pub struct Scope<P, T = ScopeEndpoint<P>> {
|
||||||
endpoint: T,
|
endpoint: T,
|
||||||
rdef: ResourceDef,
|
rdef: String,
|
||||||
services: Vec<(ResourceDef, HttpNewService<P>, Option<Guards>)>,
|
services: Vec<Box<ServiceFactory<P>>>,
|
||||||
guards: Vec<Box<Guard>>,
|
guards: Vec<Box<Guard>>,
|
||||||
default: Rc<RefCell<Option<Rc<HttpNewService<P>>>>>,
|
default: Rc<RefCell<Option<Rc<HttpNewService<P>>>>>,
|
||||||
defaults: Vec<Rc<RefCell<Option<Rc<HttpNewService<P>>>>>>,
|
|
||||||
factory_ref: Rc<RefCell<Option<ScopeFactory<P>>>>,
|
factory_ref: Rc<RefCell<Option<ScopeFactory<P>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,21 +65,20 @@ impl<P: 'static> Scope<P> {
|
||||||
/// Create a new scope
|
/// Create a new scope
|
||||||
pub fn new(path: &str) -> Scope<P> {
|
pub fn new(path: &str) -> Scope<P> {
|
||||||
let fref = Rc::new(RefCell::new(None));
|
let fref = Rc::new(RefCell::new(None));
|
||||||
let rdef = ResourceDef::prefix(&insert_slash(path));
|
|
||||||
Scope {
|
Scope {
|
||||||
endpoint: ScopeEndpoint::new(fref.clone()),
|
endpoint: ScopeEndpoint::new(fref.clone()),
|
||||||
rdef: rdef.clone(),
|
rdef: path.to_string(),
|
||||||
guards: Vec::new(),
|
guards: Vec::new(),
|
||||||
services: Vec::new(),
|
services: Vec::new(),
|
||||||
default: Rc::new(RefCell::new(None)),
|
default: Rc::new(RefCell::new(None)),
|
||||||
defaults: Vec::new(),
|
|
||||||
factory_ref: fref,
|
factory_ref: fref,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: 'static, T> Scope<P, T>
|
impl<P, T> Scope<P, T>
|
||||||
where
|
where
|
||||||
|
P: 'static,
|
||||||
T: NewService<
|
T: NewService<
|
||||||
ServiceRequest<P>,
|
ServiceRequest<P>,
|
||||||
Response = ServiceResponse,
|
Response = ServiceResponse,
|
||||||
|
@ -85,12 +86,7 @@ where
|
||||||
InitError = (),
|
InitError = (),
|
||||||
>,
|
>,
|
||||||
{
|
{
|
||||||
#[inline]
|
/// Add match guard to a scope.
|
||||||
pub(crate) fn rdef(&self) -> &ResourceDef {
|
|
||||||
&self.rdef
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add guard to a scope.
|
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use actix_web::{web, guard, App, HttpRequest, HttpResponse, extract::Path};
|
/// use actix_web::{web, guard, App, HttpRequest, HttpResponse, extract::Path};
|
||||||
|
@ -100,14 +96,14 @@ where
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().scope("/app", |scope| {
|
/// let app = App::new().service(
|
||||||
/// scope
|
/// web::scope("/app")
|
||||||
/// .guard(guard::Header("content-type", "text/plain"))
|
/// .guard(guard::Header("content-type", "text/plain"))
|
||||||
/// .route("/test1",web::get().to(index))
|
/// .route("/test1", web::get().to(index))
|
||||||
/// .route("/test2", web::post().to(|r: HttpRequest| {
|
/// .route("/test2", web::post().to(|r: HttpRequest| {
|
||||||
/// HttpResponse::MethodNotAllowed()
|
/// HttpResponse::MethodNotAllowed()
|
||||||
/// }))
|
/// }))
|
||||||
/// });
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn guard<G: Guard + 'static>(mut self, guard: G) -> Self {
|
pub fn guard<G: Guard + 'static>(mut self, guard: G) -> Self {
|
||||||
|
@ -115,10 +111,10 @@ where
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create nested scope.
|
/// Create nested service.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use actix_web::{App, HttpRequest};
|
/// use actix_web::{web, App, HttpRequest};
|
||||||
///
|
///
|
||||||
/// struct AppState;
|
/// struct AppState;
|
||||||
///
|
///
|
||||||
|
@ -127,28 +123,25 @@ where
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().scope("/app", |scope| {
|
/// let app = App::new().service(
|
||||||
/// scope.nested("/v1", |scope| scope.resource("/test1", |r| r.to(index)))
|
/// web::scope("/app").service(
|
||||||
/// });
|
/// web::scope("/v1")
|
||||||
|
/// .service(web::resource("/test1").to(index)))
|
||||||
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn nested<F>(mut self, path: &str, f: F) -> Self
|
pub fn service<F>(mut self, factory: F) -> Self
|
||||||
where
|
where
|
||||||
F: FnOnce(Scope<P>) -> Scope<P>,
|
F: HttpServiceFactory<P> + 'static,
|
||||||
{
|
{
|
||||||
let mut scope = f(Scope::new(path));
|
|
||||||
let rdef = scope.rdef().clone();
|
|
||||||
let guards = scope.take_guards();
|
|
||||||
self.defaults.push(scope.get_default());
|
|
||||||
self.services
|
self.services
|
||||||
.push((rdef, boxed::new_service(scope.into_new_service()), guards));
|
.push(Box::new(ServiceFactoryWrapper::new(factory)));
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configure route for a specific path.
|
/// Configure route for a specific path.
|
||||||
///
|
///
|
||||||
/// This is a simplified version of the `Scope::resource()` method.
|
/// This is a simplified version of the `Scope::service()` method.
|
||||||
/// This method can not be could multiple times, in that case
|
/// This method can not be could multiple times, in that case
|
||||||
/// multiple resources with one route would be registered for same resource path.
|
/// multiple resources with one route would be registered for same resource path.
|
||||||
///
|
///
|
||||||
|
@ -160,58 +153,19 @@ where
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().scope("/app", |scope| {
|
/// let app = App::new().service(
|
||||||
/// scope.route("/test1", web::get().to(index))
|
/// web::scope("/app")
|
||||||
|
/// .route("/test1", web::get().to(index))
|
||||||
/// .route("/test2", web::post().to(|| HttpResponse::MethodNotAllowed()))
|
/// .route("/test2", web::post().to(|| HttpResponse::MethodNotAllowed()))
|
||||||
/// });
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn route(self, path: &str, route: Route<P>) -> Self {
|
pub fn route(self, path: &str, mut route: Route<P>) -> Self {
|
||||||
self.resource(path, move |r| r.route(route))
|
self.service(
|
||||||
}
|
Resource::new(path)
|
||||||
|
.add_guards(route.take_guards())
|
||||||
/// configure resource for a specific path.
|
.route(route),
|
||||||
///
|
)
|
||||||
/// This method is similar to an `App::resource()` method.
|
|
||||||
/// Resources may have variable path segments. Resource path uses scope
|
|
||||||
/// path as a path prefix.
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use actix_web::*;
|
|
||||||
///
|
|
||||||
/// fn main() {
|
|
||||||
/// let app = App::new().scope("/api", |scope| {
|
|
||||||
/// scope.resource("/users/{userid}/{friend}", |r| {
|
|
||||||
/// r.route(web::get().to(|| HttpResponse::Ok()))
|
|
||||||
/// .route(web::head().to(|| HttpResponse::MethodNotAllowed()))
|
|
||||||
/// .route(web::route()
|
|
||||||
/// .guard(guard::Any(guard::Get()).or(guard::Put()))
|
|
||||||
/// .guard(guard::Header("Content-Type", "text/plain"))
|
|
||||||
/// .to(|| HttpResponse::Ok()))
|
|
||||||
/// })
|
|
||||||
/// });
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub fn resource<F, U>(mut self, path: &str, f: F) -> Self
|
|
||||||
where
|
|
||||||
F: FnOnce(Resource<P>) -> Resource<P, U>,
|
|
||||||
U: NewService<
|
|
||||||
ServiceRequest<P>,
|
|
||||||
Response = ServiceResponse,
|
|
||||||
Error = (),
|
|
||||||
InitError = (),
|
|
||||||
> + 'static,
|
|
||||||
{
|
|
||||||
// add resource
|
|
||||||
let rdef = ResourceDef::new(&insert_slash(path));
|
|
||||||
let resource = f(Resource::new());
|
|
||||||
self.defaults.push(resource.get_default());
|
|
||||||
self.services.push((
|
|
||||||
rdef,
|
|
||||||
boxed::new_service(resource.into_new_service()),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Default resource to be used if no matching route could be found.
|
/// Default resource to be used if no matching route could be found.
|
||||||
|
@ -227,7 +181,7 @@ where
|
||||||
{
|
{
|
||||||
// create and configure default resource
|
// create and configure default resource
|
||||||
self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::new_service(
|
self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::new_service(
|
||||||
f(Resource::new()).into_new_service().map_init_err(|_| ()),
|
f(Resource::new("")).into_new_service().map_init_err(|_| ()),
|
||||||
)))));
|
)))));
|
||||||
|
|
||||||
self
|
self
|
||||||
|
@ -267,62 +221,53 @@ where
|
||||||
guards: self.guards,
|
guards: self.guards,
|
||||||
services: self.services,
|
services: self.services,
|
||||||
default: self.default,
|
default: self.default,
|
||||||
defaults: self.defaults,
|
|
||||||
factory_ref: self.factory_ref,
|
factory_ref: self.factory_ref,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_default(&self) -> Rc<RefCell<Option<Rc<HttpNewService<P>>>>> {
|
|
||||||
self.default.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn take_guards(&mut self) -> Option<Vec<Box<Guard>>> {
|
|
||||||
if self.guards.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(std::mem::replace(&mut self.guards, Vec::new()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn insert_slash(path: &str) -> String {
|
impl<P, T> HttpServiceFactory<P> for Scope<P, T>
|
||||||
let mut path = path.to_owned();
|
|
||||||
if !path.is_empty() && !path.starts_with('/') {
|
|
||||||
path.insert(0, '/');
|
|
||||||
};
|
|
||||||
path
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, T> IntoNewService<T, ServiceRequest<P>> for Scope<P, T>
|
|
||||||
where
|
where
|
||||||
|
P: 'static,
|
||||||
T: NewService<
|
T: NewService<
|
||||||
ServiceRequest<P>,
|
ServiceRequest<P>,
|
||||||
Response = ServiceResponse,
|
Response = ServiceResponse,
|
||||||
Error = (),
|
Error = (),
|
||||||
InitError = (),
|
InitError = (),
|
||||||
>,
|
> + 'static,
|
||||||
{
|
{
|
||||||
fn into_new_service(self) -> T {
|
fn register(self, config: &mut AppConfig<P>) {
|
||||||
// update resource default service
|
if self.default.borrow().is_none() {
|
||||||
if let Some(ref d) = *self.default.as_ref().borrow() {
|
*self.default.borrow_mut() = Some(config.default_service());
|
||||||
for default in &self.defaults {
|
|
||||||
if default.borrow_mut().is_none() {
|
|
||||||
*default.borrow_mut() = Some(d.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// register services
|
||||||
|
let mut cfg = config.clone_config();
|
||||||
|
self.services
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|mut srv| srv.register(&mut cfg));
|
||||||
|
|
||||||
*self.factory_ref.borrow_mut() = Some(ScopeFactory {
|
*self.factory_ref.borrow_mut() = Some(ScopeFactory {
|
||||||
default: self.default.clone(),
|
default: self.default.clone(),
|
||||||
services: Rc::new(
|
services: Rc::new(
|
||||||
self.services
|
cfg.into_services()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(rdef, srv, guards)| (rdef, srv, RefCell::new(guards)))
|
.map(|(rdef, srv, guards)| (rdef, srv, RefCell::new(guards)))
|
||||||
.collect(),
|
.collect(),
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
self.endpoint
|
let guards = if self.guards.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(self.guards)
|
||||||
|
};
|
||||||
|
let rdef = if config.is_root() {
|
||||||
|
ResourceDef::prefix(&insert_slash(&self.rdef))
|
||||||
|
} else {
|
||||||
|
ResourceDef::prefix(&insert_slash(&self.rdef))
|
||||||
|
};
|
||||||
|
config.register_service(rdef, guards, self.endpoint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -504,19 +449,22 @@ impl<P: 'static> NewService<ServiceRequest<P>> for ScopeEndpoint<P> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use actix_http::body::{Body, ResponseBody};
|
use actix_service::Service;
|
||||||
use actix_http::http::{Method, StatusCode};
|
|
||||||
use actix_service::{IntoNewService, NewService, Service};
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
|
||||||
use crate::test::{self, block_on, TestRequest};
|
use crate::body::{Body, ResponseBody};
|
||||||
|
use crate::http::{Method, StatusCode};
|
||||||
|
use crate::test::{block_on, init_service, TestRequest};
|
||||||
use crate::{guard, web, App, HttpRequest, HttpResponse};
|
use crate::{guard, web, App, HttpRequest, HttpResponse};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scope() {
|
fn test_scope() {
|
||||||
let mut srv = test::init_service(App::new().scope("/app", |scope| {
|
let mut srv = init_service(
|
||||||
scope.resource("/path1", |r| r.to(|| HttpResponse::Ok()))
|
App::new().service(
|
||||||
}));
|
web::scope("/app")
|
||||||
|
.service(web::resource("/path1").to(|| HttpResponse::Ok())),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app/path1").to_request();
|
let req = TestRequest::with_uri("/app/path1").to_request();
|
||||||
let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
|
@ -525,11 +473,13 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scope_root() {
|
fn test_scope_root() {
|
||||||
let mut srv = test::init_service(App::new().scope("/app", |scope| {
|
let mut srv = init_service(
|
||||||
scope
|
App::new().service(
|
||||||
.resource("", |r| r.to(|| HttpResponse::Ok()))
|
web::scope("/app")
|
||||||
.resource("/", |r| r.to(|| HttpResponse::Created()))
|
.service(web::resource("").to(|| HttpResponse::Ok()))
|
||||||
}));
|
.service(web::resource("/").to(|| HttpResponse::Created())),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app").to_request();
|
let req = TestRequest::with_uri("/app").to_request();
|
||||||
let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
|
@ -542,9 +492,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scope_root2() {
|
fn test_scope_root2() {
|
||||||
let mut srv = test::init_service(App::new().scope("/app/", |scope| {
|
let mut srv = init_service(App::new().service(
|
||||||
scope.resource("", |r| r.to(|| HttpResponse::Ok()))
|
web::scope("/app/").service(web::resource("").to(|| HttpResponse::Ok())),
|
||||||
}));
|
));
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app").to_request();
|
let req = TestRequest::with_uri("/app").to_request();
|
||||||
let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
|
@ -557,12 +507,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scope_root3() {
|
fn test_scope_root3() {
|
||||||
let app = App::new()
|
let mut srv = init_service(App::new().service(
|
||||||
.scope("/app/", |scope| {
|
web::scope("/app/").service(web::resource("/").to(|| HttpResponse::Ok())),
|
||||||
scope.resource("/", |r| r.to(|| HttpResponse::Ok()))
|
));
|
||||||
})
|
|
||||||
.into_new_service();
|
|
||||||
let mut srv = block_on(app.new_service(&())).unwrap();
|
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app").to_request();
|
let req = TestRequest::with_uri("/app").to_request();
|
||||||
let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
|
@ -575,15 +522,13 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scope_route() {
|
fn test_scope_route() {
|
||||||
let app = App::new()
|
let mut srv = init_service(
|
||||||
.scope("app", |scope| {
|
App::new().service(
|
||||||
scope.resource("/path1", |r| {
|
web::scope("app")
|
||||||
r.route(web::get().to(|| HttpResponse::Ok()))
|
.route("/path1", web::get().to(|| HttpResponse::Ok()))
|
||||||
.route(web::delete().to(|| HttpResponse::Ok()))
|
.route("/path1", web::delete().to(|| HttpResponse::Ok())),
|
||||||
})
|
),
|
||||||
})
|
);
|
||||||
.into_new_service();
|
|
||||||
let mut srv = block_on(app.new_service(&())).unwrap();
|
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app/path1").to_request();
|
let req = TestRequest::with_uri("/app/path1").to_request();
|
||||||
let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
|
@ -604,15 +549,15 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scope_route_without_leading_slash() {
|
fn test_scope_route_without_leading_slash() {
|
||||||
let app = App::new()
|
let mut srv = init_service(
|
||||||
.scope("app", |scope| {
|
App::new().service(
|
||||||
scope.resource("path1", |r| {
|
web::scope("app").service(
|
||||||
r.route(web::get().to(|| HttpResponse::Ok()))
|
web::resource("path1")
|
||||||
.route(web::delete().to(|| HttpResponse::Ok()))
|
.route(web::get().to(|| HttpResponse::Ok()))
|
||||||
})
|
.route(web::delete().to(|| HttpResponse::Ok())),
|
||||||
})
|
),
|
||||||
.into_new_service();
|
),
|
||||||
let mut srv = block_on(app.new_service(&())).unwrap();
|
);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app/path1").to_request();
|
let req = TestRequest::with_uri("/app/path1").to_request();
|
||||||
let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
|
@ -633,14 +578,13 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scope_guard() {
|
fn test_scope_guard() {
|
||||||
let app = App::new()
|
let mut srv = init_service(
|
||||||
.scope("/app", |scope| {
|
App::new().service(
|
||||||
scope
|
web::scope("/app")
|
||||||
.guard(guard::Get())
|
.guard(guard::Get())
|
||||||
.resource("/path1", |r| r.to(|| HttpResponse::Ok()))
|
.service(web::resource("/path1").to(|| HttpResponse::Ok())),
|
||||||
})
|
),
|
||||||
.into_new_service();
|
);
|
||||||
let mut srv = block_on(app.new_service(&())).unwrap();
|
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app/path1")
|
let req = TestRequest::with_uri("/app/path1")
|
||||||
.method(Method::POST)
|
.method(Method::POST)
|
||||||
|
@ -657,17 +601,13 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scope_variable_segment() {
|
fn test_scope_variable_segment() {
|
||||||
let app = App::new()
|
let mut srv =
|
||||||
.scope("/ab-{project}", |scope| {
|
init_service(App::new().service(web::scope("/ab-{project}").service(
|
||||||
scope.resource("/path1", |r| {
|
web::resource("/path1").to(|r: HttpRequest| {
|
||||||
r.to(|r: HttpRequest| {
|
HttpResponse::Ok()
|
||||||
HttpResponse::Ok()
|
.body(format!("project: {}", &r.match_info()["project"]))
|
||||||
.body(format!("project: {}", &r.match_info()["project"]))
|
}),
|
||||||
})
|
)));
|
||||||
})
|
|
||||||
})
|
|
||||||
.into_new_service();
|
|
||||||
let mut srv = block_on(app.new_service(&())).unwrap();
|
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/ab-project1/path1").to_request();
|
let req = TestRequest::with_uri("/ab-project1/path1").to_request();
|
||||||
let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
|
@ -688,14 +628,14 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_nested_scope() {
|
fn test_nested_scope() {
|
||||||
let app = App::new()
|
let mut srv = init_service(
|
||||||
.scope("/app", |scope| {
|
App::new().service(
|
||||||
scope.nested("/t1", |scope| {
|
web::scope("/app")
|
||||||
scope.resource("/path1", |r| r.to(|| HttpResponse::Created()))
|
.service(web::scope("/t1").service(
|
||||||
})
|
web::resource("/path1").to(|| HttpResponse::Created()),
|
||||||
})
|
)),
|
||||||
.into_new_service();
|
),
|
||||||
let mut srv = block_on(app.new_service(&())).unwrap();
|
);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app/t1/path1").to_request();
|
let req = TestRequest::with_uri("/app/t1/path1").to_request();
|
||||||
let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
|
@ -704,14 +644,14 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_nested_scope_no_slash() {
|
fn test_nested_scope_no_slash() {
|
||||||
let app = App::new()
|
let mut srv = init_service(
|
||||||
.scope("/app", |scope| {
|
App::new().service(
|
||||||
scope.nested("t1", |scope| {
|
web::scope("/app")
|
||||||
scope.resource("/path1", |r| r.to(|| HttpResponse::Created()))
|
.service(web::scope("t1").service(
|
||||||
})
|
web::resource("/path1").to(|| HttpResponse::Created()),
|
||||||
})
|
)),
|
||||||
.into_new_service();
|
),
|
||||||
let mut srv = block_on(app.new_service(&())).unwrap();
|
);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app/t1/path1").to_request();
|
let req = TestRequest::with_uri("/app/t1/path1").to_request();
|
||||||
let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
|
@ -720,16 +660,15 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_nested_scope_root() {
|
fn test_nested_scope_root() {
|
||||||
let app = App::new()
|
let mut srv = init_service(
|
||||||
.scope("/app", |scope| {
|
App::new().service(
|
||||||
scope.nested("/t1", |scope| {
|
web::scope("/app").service(
|
||||||
scope
|
web::scope("/t1")
|
||||||
.resource("", |r| r.to(|| HttpResponse::Ok()))
|
.service(web::resource("").to(|| HttpResponse::Ok()))
|
||||||
.resource("/", |r| r.to(|| HttpResponse::Created()))
|
.service(web::resource("/").to(|| HttpResponse::Created())),
|
||||||
})
|
),
|
||||||
})
|
),
|
||||||
.into_new_service();
|
);
|
||||||
let mut srv = block_on(app.new_service(&())).unwrap();
|
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app/t1").to_request();
|
let req = TestRequest::with_uri("/app/t1").to_request();
|
||||||
let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
|
@ -742,16 +681,15 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_nested_scope_filter() {
|
fn test_nested_scope_filter() {
|
||||||
let app = App::new()
|
let mut srv = init_service(
|
||||||
.scope("/app", |scope| {
|
App::new().service(
|
||||||
scope.nested("/t1", |scope| {
|
web::scope("/app").service(
|
||||||
scope
|
web::scope("/t1")
|
||||||
.guard(guard::Get())
|
.guard(guard::Get())
|
||||||
.resource("/path1", |r| r.to(|| HttpResponse::Ok()))
|
.service(web::resource("/path1").to(|| HttpResponse::Ok())),
|
||||||
})
|
),
|
||||||
})
|
),
|
||||||
.into_new_service();
|
);
|
||||||
let mut srv = block_on(app.new_service(&())).unwrap();
|
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app/t1/path1")
|
let req = TestRequest::with_uri("/app/t1/path1")
|
||||||
.method(Method::POST)
|
.method(Method::POST)
|
||||||
|
@ -768,21 +706,14 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_nested_scope_with_variable_segment() {
|
fn test_nested_scope_with_variable_segment() {
|
||||||
let app = App::new()
|
let mut srv = init_service(App::new().service(web::scope("/app").service(
|
||||||
.scope("/app", |scope| {
|
web::scope("/{project_id}").service(web::resource("/path1").to(
|
||||||
scope.nested("/{project_id}", |scope| {
|
|r: HttpRequest| {
|
||||||
scope.resource("/path1", |r| {
|
HttpResponse::Created()
|
||||||
r.to(|r: HttpRequest| {
|
.body(format!("project: {}", &r.match_info()["project_id"]))
|
||||||
HttpResponse::Created().body(format!(
|
},
|
||||||
"project: {}",
|
)),
|
||||||
&r.match_info()["project_id"]
|
)));
|
||||||
))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.into_new_service();
|
|
||||||
let mut srv = block_on(app.new_service(&())).unwrap();
|
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app/project_1/path1").to_request();
|
let req = TestRequest::with_uri("/app/project_1/path1").to_request();
|
||||||
let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
|
@ -799,24 +730,17 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_nested2_scope_with_variable_segment() {
|
fn test_nested2_scope_with_variable_segment() {
|
||||||
let app = App::new()
|
let mut srv = init_service(App::new().service(web::scope("/app").service(
|
||||||
.scope("/app", |scope| {
|
web::scope("/{project}").service(web::scope("/{id}").service(
|
||||||
scope.nested("/{project}", |scope| {
|
web::resource("/path1").to(|r: HttpRequest| {
|
||||||
scope.nested("/{id}", |scope| {
|
HttpResponse::Created().body(format!(
|
||||||
scope.resource("/path1", |r| {
|
"project: {} - {}",
|
||||||
r.to(|r: HttpRequest| {
|
&r.match_info()["project"],
|
||||||
HttpResponse::Created().body(format!(
|
&r.match_info()["id"],
|
||||||
"project: {} - {}",
|
))
|
||||||
&r.match_info()["project"],
|
}),
|
||||||
&r.match_info()["id"],
|
)),
|
||||||
))
|
)));
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.into_new_service();
|
|
||||||
let mut srv = block_on(app.new_service(&())).unwrap();
|
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app/test/1/path1").to_request();
|
let req = TestRequest::with_uri("/app/test/1/path1").to_request();
|
||||||
let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
|
@ -837,14 +761,13 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_default_resource() {
|
fn test_default_resource() {
|
||||||
let app = App::new()
|
let mut srv = init_service(
|
||||||
.scope("/app", |scope| {
|
App::new().service(
|
||||||
scope
|
web::scope("/app")
|
||||||
.resource("/path1", |r| r.to(|| HttpResponse::Ok()))
|
.service(web::resource("/path1").to(|| HttpResponse::Ok()))
|
||||||
.default_resource(|r| r.to(|| HttpResponse::BadRequest()))
|
.default_resource(|r| r.to(|| HttpResponse::BadRequest())),
|
||||||
})
|
),
|
||||||
.into_new_service();
|
);
|
||||||
let mut srv = block_on(app.new_service(&())).unwrap();
|
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/app/path2").to_request();
|
let req = TestRequest::with_uri("/app/path2").to_request();
|
||||||
let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
|
@ -857,14 +780,15 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_default_resource_propagation() {
|
fn test_default_resource_propagation() {
|
||||||
let app = App::new()
|
let mut srv = init_service(
|
||||||
.scope("/app1", |scope| {
|
App::new()
|
||||||
scope.default_resource(|r| r.to(|| HttpResponse::BadRequest()))
|
.service(
|
||||||
})
|
web::scope("/app1")
|
||||||
.scope("/app2", |scope| scope)
|
.default_resource(|r| r.to(|| HttpResponse::BadRequest())),
|
||||||
.default_resource(|r| r.to(|| HttpResponse::MethodNotAllowed()))
|
)
|
||||||
.into_new_service();
|
.service(web::scope("/app2"))
|
||||||
let mut srv = block_on(app.new_service(&())).unwrap();
|
.default_resource(|r| r.to(|| HttpResponse::MethodNotAllowed())),
|
||||||
|
);
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/non-exist").to_request();
|
let req = TestRequest::with_uri("/non-exist").to_request();
|
||||||
let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
|
|
|
@ -33,20 +33,19 @@ struct Config {
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use std::io;
|
/// use std::io;
|
||||||
/// use actix_web::{App, HttpResponse, HttpServer};
|
/// use actix_web::{web, App, HttpResponse, HttpServer};
|
||||||
///
|
///
|
||||||
/// fn main() -> io::Result<()> {
|
/// fn main() -> io::Result<()> {
|
||||||
/// let sys = actix_rt::System::new("example"); // <- create Actix runtime
|
/// let sys = actix_rt::System::new("example"); // <- create Actix runtime
|
||||||
///
|
///
|
||||||
/// HttpServer::new(
|
/// HttpServer::new(
|
||||||
/// || App::new()
|
/// || App::new()
|
||||||
/// .resource("/", |r| r.to(|| HttpResponse::Ok())))
|
/// .service(web::resource("/").to(|| HttpResponse::Ok())))
|
||||||
/// .bind("127.0.0.1:59090")?
|
/// .bind("127.0.0.1:59090")?
|
||||||
/// .start();
|
/// .start();
|
||||||
///
|
///
|
||||||
/// # actix_rt::System::current().stop();
|
/// # actix_rt::System::current().stop();
|
||||||
/// sys.run();
|
/// sys.run()
|
||||||
/// Ok(())
|
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct HttpServer<F, I, S, B>
|
pub struct HttpServer<F, I, S, B>
|
||||||
|
@ -448,17 +447,17 @@ where
|
||||||
/// configured.
|
/// configured.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use actix_web::{App, HttpResponse, HttpServer};
|
/// use std::io;
|
||||||
|
/// use actix_web::{web, App, HttpResponse, HttpServer};
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() -> io::Result<()> {
|
||||||
/// let sys = actix_rt::System::new("example"); // <- create Actix system
|
/// let sys = actix_rt::System::new("example"); // <- create Actix system
|
||||||
///
|
///
|
||||||
/// HttpServer::new(|| App::new().resource("/", |r| r.to(|| HttpResponse::Ok())))
|
/// HttpServer::new(|| App::new().service(web::resource("/").to(|| HttpResponse::Ok())))
|
||||||
/// .bind("127.0.0.1:0")
|
/// .bind("127.0.0.1:0")?
|
||||||
/// .expect("Can not bind to 127.0.0.1:0")
|
|
||||||
/// .start();
|
/// .start();
|
||||||
/// # actix_rt::System::current().stop();
|
/// # actix_rt::System::current().stop();
|
||||||
/// sys.run(); // <- Run actix system, this method starts all async processes
|
/// sys.run() // <- Run actix system, this method starts all async processes
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn start(mut self) -> Server {
|
pub fn start(mut self) -> Server {
|
||||||
|
@ -472,20 +471,23 @@ where
|
||||||
///
|
///
|
||||||
/// This methods panics if no socket addresses get bound.
|
/// This methods panics if no socket addresses get bound.
|
||||||
///
|
///
|
||||||
/// ```rust,ignore
|
/// ```rust
|
||||||
/// use actix_web::{App, HttpResponse, HttpServer};
|
/// use std::io;
|
||||||
|
/// use actix_web::{web, App, HttpResponse, HttpServer};
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() -> io::Result<()> {
|
||||||
/// HttpServer::new(|| App::new().resource("/", |r| r.to(|| HttpResponse::Ok())))
|
/// # std::thread::spawn(|| {
|
||||||
/// .bind("127.0.0.1:0")
|
/// HttpServer::new(|| App::new().service(web::resource("/").to(|| HttpResponse::Ok())))
|
||||||
/// .expect("Can not bind to 127.0.0.1:0")
|
/// .bind("127.0.0.1:0")?
|
||||||
/// .run();
|
/// .run()
|
||||||
|
/// # });
|
||||||
|
/// # Ok(())
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn run(self) {
|
pub fn run(self) -> io::Result<()> {
|
||||||
let sys = System::new("http-server");
|
let sys = System::new("http-server");
|
||||||
self.start();
|
self.start();
|
||||||
sys.run();
|
sys.run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::cell::{Ref, RefMut};
|
use std::cell::{Ref, RefMut};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::marker::PhantomData;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use actix_http::body::{Body, MessageBody, ResponseBody};
|
use actix_http::body::{Body, MessageBody, ResponseBody};
|
||||||
|
@ -12,8 +13,42 @@ use actix_http::{
|
||||||
use actix_router::{Path, Resource, Url};
|
use actix_router::{Path, Resource, Url};
|
||||||
use futures::future::{ok, FutureResult, IntoFuture};
|
use futures::future::{ok, FutureResult, IntoFuture};
|
||||||
|
|
||||||
|
use crate::config::AppConfig;
|
||||||
use crate::request::HttpRequest;
|
use crate::request::HttpRequest;
|
||||||
|
|
||||||
|
pub trait HttpServiceFactory<P> {
|
||||||
|
fn register(self, config: &mut AppConfig<P>);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) trait ServiceFactory<P> {
|
||||||
|
fn register(&mut self, config: &mut AppConfig<P>);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct ServiceFactoryWrapper<T, P> {
|
||||||
|
factory: Option<T>,
|
||||||
|
_t: PhantomData<P>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, P> ServiceFactoryWrapper<T, P> {
|
||||||
|
pub fn new(factory: T) -> Self {
|
||||||
|
Self {
|
||||||
|
factory: Some(factory),
|
||||||
|
_t: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, P> ServiceFactory<P> for ServiceFactoryWrapper<T, P>
|
||||||
|
where
|
||||||
|
T: HttpServiceFactory<P>,
|
||||||
|
{
|
||||||
|
fn register(&mut self, config: &mut AppConfig<P>) {
|
||||||
|
if let Some(item) = self.factory.take() {
|
||||||
|
item.register(config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ServiceRequest<P = PayloadStream> {
|
pub struct ServiceRequest<P = PayloadStream> {
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
payload: Payload<P>,
|
payload: Payload<P>,
|
||||||
|
|
|
@ -40,7 +40,8 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
|
||||||
fn test_body() {
|
fn test_body() {
|
||||||
let mut srv = TestServer::new(|| {
|
let mut srv = TestServer::new(|| {
|
||||||
h1::H1Service::new(
|
h1::H1Service::new(
|
||||||
App::new().resource("/", |r| r.route(web::to(|| Response::Ok().body(STR)))),
|
App::new()
|
||||||
|
.service(web::resource("/").route(web::to(|| Response::Ok().body(STR)))),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -59,7 +60,7 @@ fn test_body_gzip() {
|
||||||
h1::H1Service::new(
|
h1::H1Service::new(
|
||||||
App::new()
|
App::new()
|
||||||
.middleware(middleware::Compress::new(ContentEncoding::Gzip))
|
.middleware(middleware::Compress::new(ContentEncoding::Gzip))
|
||||||
.resource("/", |r| r.route(web::to(|| Response::Ok().body(STR)))),
|
.service(web::resource("/").route(web::to(|| Response::Ok().body(STR)))),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -87,9 +88,10 @@ fn test_body_gzip_large() {
|
||||||
h1::H1Service::new(
|
h1::H1Service::new(
|
||||||
App::new()
|
App::new()
|
||||||
.middleware(middleware::Compress::new(ContentEncoding::Gzip))
|
.middleware(middleware::Compress::new(ContentEncoding::Gzip))
|
||||||
.resource("/", |r| {
|
.service(
|
||||||
r.route(web::to(move || Response::Ok().body(data.clone())))
|
web::resource("/")
|
||||||
}),
|
.route(web::to(move || Response::Ok().body(data.clone()))),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -120,9 +122,10 @@ fn test_body_gzip_large_random() {
|
||||||
h1::H1Service::new(
|
h1::H1Service::new(
|
||||||
App::new()
|
App::new()
|
||||||
.middleware(middleware::Compress::new(ContentEncoding::Gzip))
|
.middleware(middleware::Compress::new(ContentEncoding::Gzip))
|
||||||
.resource("/", |r| {
|
.service(
|
||||||
r.route(web::to(move || Response::Ok().body(data.clone())))
|
web::resource("/")
|
||||||
}),
|
.route(web::to(move || Response::Ok().body(data.clone()))),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -147,13 +150,11 @@ fn test_body_chunked_implicit() {
|
||||||
h1::H1Service::new(
|
h1::H1Service::new(
|
||||||
App::new()
|
App::new()
|
||||||
.middleware(middleware::Compress::new(ContentEncoding::Gzip))
|
.middleware(middleware::Compress::new(ContentEncoding::Gzip))
|
||||||
.resource("/", |r| {
|
.service(web::resource("/").route(web::get().to(move || {
|
||||||
r.route(web::get().to(move || {
|
Response::Ok().streaming(once(Ok::<_, Error>(Bytes::from_static(
|
||||||
Response::Ok().streaming(once(Ok::<_, Error>(
|
STR.as_ref(),
|
||||||
Bytes::from_static(STR.as_ref()),
|
))))
|
||||||
)))
|
}))),
|
||||||
}))
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -181,13 +182,11 @@ fn test_body_br_streaming() {
|
||||||
h1::H1Service::new(
|
h1::H1Service::new(
|
||||||
App::new()
|
App::new()
|
||||||
.middleware(middleware::Compress::new(ContentEncoding::Br))
|
.middleware(middleware::Compress::new(ContentEncoding::Br))
|
||||||
.resource("/", |r| {
|
.service(web::resource("/").route(web::to(move || {
|
||||||
r.route(web::to(move || {
|
Response::Ok().streaming(once(Ok::<_, Error>(Bytes::from_static(
|
||||||
Response::Ok().streaming(once(Ok::<_, Error>(
|
STR.as_ref(),
|
||||||
Bytes::from_static(STR.as_ref()),
|
))))
|
||||||
)))
|
}))),
|
||||||
}))
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -208,9 +207,9 @@ fn test_body_br_streaming() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_head_binary() {
|
fn test_head_binary() {
|
||||||
let mut srv = TestServer::new(move || {
|
let mut srv = TestServer::new(move || {
|
||||||
h1::H1Service::new(App::new().resource("/", |r| {
|
h1::H1Service::new(App::new().service(web::resource("/").route(
|
||||||
r.route(web::head().to(move || Response::Ok().content_length(100).body(STR)))
|
web::head().to(move || Response::Ok().content_length(100).body(STR)),
|
||||||
}))
|
)))
|
||||||
});
|
});
|
||||||
|
|
||||||
let request = srv.head().finish().unwrap();
|
let request = srv.head().finish().unwrap();
|
||||||
|
@ -230,14 +229,14 @@ fn test_head_binary() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_no_chunking() {
|
fn test_no_chunking() {
|
||||||
let mut srv = TestServer::new(move || {
|
let mut srv = TestServer::new(move || {
|
||||||
h1::H1Service::new(App::new().resource("/", |r| {
|
h1::H1Service::new(App::new().service(web::resource("/").route(web::to(
|
||||||
r.route(web::to(move || {
|
move || {
|
||||||
Response::Ok()
|
Response::Ok()
|
||||||
.no_chunking()
|
.no_chunking()
|
||||||
.content_length(STR.len() as u64)
|
.content_length(STR.len() as u64)
|
||||||
.streaming(once(Ok::<_, Error>(Bytes::from_static(STR.as_ref()))))
|
.streaming(once(Ok::<_, Error>(Bytes::from_static(STR.as_ref()))))
|
||||||
}))
|
},
|
||||||
}))
|
))))
|
||||||
});
|
});
|
||||||
|
|
||||||
let request = srv.get().finish().unwrap();
|
let request = srv.get().finish().unwrap();
|
||||||
|
@ -256,7 +255,9 @@ fn test_body_deflate() {
|
||||||
h1::H1Service::new(
|
h1::H1Service::new(
|
||||||
App::new()
|
App::new()
|
||||||
.middleware(middleware::Compress::new(ContentEncoding::Deflate))
|
.middleware(middleware::Compress::new(ContentEncoding::Deflate))
|
||||||
.resource("/", |r| r.route(web::to(move || Response::Ok().body(STR)))),
|
.service(
|
||||||
|
web::resource("/").route(web::to(move || Response::Ok().body(STR))),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -281,7 +282,9 @@ fn test_body_brotli() {
|
||||||
h1::H1Service::new(
|
h1::H1Service::new(
|
||||||
App::new()
|
App::new()
|
||||||
.middleware(middleware::Compress::new(ContentEncoding::Br))
|
.middleware(middleware::Compress::new(ContentEncoding::Br))
|
||||||
.resource("/", |r| r.route(web::to(move || Response::Ok().body(STR)))),
|
.service(
|
||||||
|
web::resource("/").route(web::to(move || Response::Ok().body(STR))),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue