mirror of
https://github.com/actix/actix-web.git
synced 2024-11-10 10:59:33 +00:00
allow to use custom state for scope
This commit is contained in:
parent
eefbe19651
commit
25b245ac72
1 changed files with 92 additions and 9 deletions
83
src/scope.rs
83
src/scope.rs
|
@ -42,6 +42,7 @@ type ScopeResources<S> = Rc<Vec<(Resource, Rc<UnsafeCell<ResourceHandler<S>>>)>>
|
|||
/// * /app/path3 - `HEAD` requests
|
||||
///
|
||||
pub struct Scope<S: 'static> {
|
||||
handler: Option<UnsafeCell<Box<RouteHandler<S>>>>,
|
||||
middlewares: Rc<Vec<Box<Middleware<S>>>>,
|
||||
default: Rc<UnsafeCell<ResourceHandler<S>>>,
|
||||
resources: ScopeResources<S>,
|
||||
|
@ -56,12 +57,54 @@ impl<S: 'static> Default for Scope<S> {
|
|||
impl<S: 'static> Scope<S> {
|
||||
pub fn new() -> Scope<S> {
|
||||
Scope {
|
||||
handler: None,
|
||||
resources: Rc::new(Vec::new()),
|
||||
middlewares: Rc::new(Vec::new()),
|
||||
default: Rc::new(UnsafeCell::new(ResourceHandler::default_not_found())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create scope with new state.
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate actix_web;
|
||||
/// use actix_web::{http, App, HttpRequest, HttpResponse, Path};
|
||||
///
|
||||
/// struct AppState;
|
||||
///
|
||||
/// fn index(req: HttpRequest<AppState>) -> &'static str {
|
||||
/// "Welcome!"
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// let app = App::new()
|
||||
/// .scope("/app", |scope| {
|
||||
/// scope.with_state(AppState, |scope| {
|
||||
/// scope.resource("/test1", |r| r.f(index))
|
||||
/// })
|
||||
/// });
|
||||
/// }
|
||||
/// ```
|
||||
pub fn with_state<F, T: 'static>(mut self, state: T, f: F) -> Scope<S>
|
||||
where
|
||||
F: FnOnce(Scope<T>) -> Scope<T>,
|
||||
{
|
||||
let scope = Scope {
|
||||
handler: None,
|
||||
resources: Rc::new(Vec::new()),
|
||||
middlewares: Rc::new(Vec::new()),
|
||||
default: Rc::new(UnsafeCell::new(ResourceHandler::default_not_found())),
|
||||
};
|
||||
let scope = f(scope);
|
||||
|
||||
self.handler = Some(UnsafeCell::new(Box::new(Wrapper {
|
||||
scope,
|
||||
state: Rc::new(state),
|
||||
})));
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Configure route for a specific path.
|
||||
///
|
||||
/// This is a simplified version of the `Scope::resource()` method.
|
||||
|
@ -184,6 +227,7 @@ impl<S: 'static> RouteHandler<S> for Scope<S> {
|
|||
let path = unsafe { &*(&req.match_info()["tail"] as *const _) };
|
||||
let path = if path == "" { "/" } else { path };
|
||||
|
||||
// recognize paths
|
||||
for &(ref pattern, ref resource) in self.resources.iter() {
|
||||
if pattern.match_with_params(path, req.match_info_mut()) {
|
||||
let default = unsafe { &mut *self.default.as_ref().get() };
|
||||
|
@ -202,6 +246,12 @@ impl<S: 'static> RouteHandler<S> for Scope<S> {
|
|||
}
|
||||
}
|
||||
|
||||
// nested scope
|
||||
if let Some(ref handler) = self.handler {
|
||||
let hnd: &mut RouteHandler<_> = unsafe { (&mut *(handler.get())).as_mut() };
|
||||
hnd.handle(req)
|
||||
} else {
|
||||
// default handler
|
||||
let default = unsafe { &mut *self.default.as_ref().get() };
|
||||
if self.middlewares.is_empty() {
|
||||
default.handle(req, None)
|
||||
|
@ -214,6 +264,19 @@ impl<S: 'static> RouteHandler<S> for Scope<S> {
|
|||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Wrapper<S: 'static> {
|
||||
state: Rc<S>,
|
||||
scope: Scope<S>,
|
||||
}
|
||||
|
||||
impl<S: 'static, S2: 'static> RouteHandler<S2> for Wrapper<S> {
|
||||
fn handle(&mut self, req: HttpRequest<S2>) -> Reply {
|
||||
self.scope
|
||||
.handle(req.change_state(Rc::clone(&self.state)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Compose resource level middlewares with route handler.
|
||||
|
@ -574,6 +637,26 @@ mod tests {
|
|||
assert_eq!(resp.as_response().unwrap().status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_with_state() {
|
||||
struct State;
|
||||
|
||||
let mut app = App::new()
|
||||
.scope("/app", |scope| {
|
||||
scope.with_state(State, |scope| {
|
||||
scope.resource("/path1", |r| r.f(|_| HttpResponse::Created()))
|
||||
})
|
||||
})
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/app/path1").finish();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(
|
||||
resp.as_response().unwrap().status(),
|
||||
StatusCode::CREATED
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_resource() {
|
||||
let mut app = App::new()
|
||||
|
|
Loading…
Reference in a new issue