1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-27 17:52:56 +01:00

Merge pull request #339 from joshleeb/propogate-scope-default-resource

Propagate scope default resource
This commit is contained in:
Nikolay Kim 2018-06-21 15:40:02 +06:00 committed by GitHub
commit 5a9992736f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 60 additions and 8 deletions

View File

@ -25,6 +25,8 @@
* `HttpRequest::url_for_static()` for a named route with no variables segments * `HttpRequest::url_for_static()` for a named route with no variables segments
* Propagation of the application's default resource to scopes that haven't set a default resource.
### Changed ### Changed

View File

@ -29,7 +29,7 @@ pub struct HttpApplication<S = ()> {
#[doc(hidden)] #[doc(hidden)]
pub struct Inner<S> { pub struct Inner<S> {
prefix: usize, prefix: usize,
default: ResourceHandler<S>, default: Rc<RefCell<ResourceHandler<S>>>,
encoding: ContentEncoding, encoding: ContentEncoding,
resources: Vec<ResourceHandler<S>>, resources: Vec<ResourceHandler<S>>,
handlers: Vec<PrefixHandlerType<S>>, handlers: Vec<PrefixHandlerType<S>>,
@ -51,7 +51,7 @@ impl<S: 'static> PipelineHandler<S> for Inner<S> {
match htype { match htype {
HandlerType::Normal(idx) => match self.resources[idx].handle(req) { HandlerType::Normal(idx) => match self.resources[idx].handle(req) {
Ok(result) => result, Ok(result) => result,
Err(req) => match self.default.handle(req) { Err(req) => match self.default.borrow_mut().handle(req) {
Ok(result) => result, Ok(result) => result,
Err(_) => AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)), Err(_) => AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)),
}, },
@ -60,7 +60,7 @@ impl<S: 'static> PipelineHandler<S> for Inner<S> {
PrefixHandlerType::Handler(_, ref mut hnd) => hnd.handle(req), PrefixHandlerType::Handler(_, ref mut hnd) => hnd.handle(req),
PrefixHandlerType::Scope(_, ref mut hnd, _) => hnd.handle(req), PrefixHandlerType::Scope(_, ref mut hnd, _) => hnd.handle(req),
}, },
HandlerType::Default => match self.default.handle(req) { HandlerType::Default => match self.default.borrow_mut().handle(req) {
Ok(result) => result, Ok(result) => result,
Err(_) => AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)), Err(_) => AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)),
}, },
@ -172,7 +172,7 @@ struct ApplicationParts<S> {
state: S, state: S,
prefix: String, prefix: String,
settings: ServerSettings, settings: ServerSettings,
default: ResourceHandler<S>, default: Rc<RefCell<ResourceHandler<S>>>,
resources: Vec<(Resource, Option<ResourceHandler<S>>)>, resources: Vec<(Resource, Option<ResourceHandler<S>>)>,
handlers: Vec<PrefixHandlerType<S>>, handlers: Vec<PrefixHandlerType<S>>,
external: HashMap<String, Resource>, external: HashMap<String, Resource>,
@ -223,7 +223,7 @@ where
state, state,
prefix: "/".to_owned(), prefix: "/".to_owned(),
settings: ServerSettings::default(), settings: ServerSettings::default(),
default: ResourceHandler::default_not_found(), default: Rc::new(RefCell::new(ResourceHandler::default_not_found())),
resources: Vec::new(), resources: Vec::new(),
handlers: Vec::new(), handlers: Vec::new(),
external: HashMap::new(), external: HashMap::new(),
@ -474,7 +474,7 @@ where
{ {
{ {
let parts = self.parts.as_mut().expect("Use after finish"); let parts = self.parts.as_mut().expect("Use after finish");
f(&mut parts.default); f(&mut parts.default.borrow_mut());
} }
self self
} }
@ -615,7 +615,7 @@ where
/// Finish application configuration and create `HttpHandler` object. /// Finish application configuration and create `HttpHandler` object.
pub fn finish(&mut self) -> HttpApplication<S> { pub fn finish(&mut self) -> HttpApplication<S> {
let parts = self.parts.take().expect("Use after finish"); let mut parts = self.parts.take().expect("Use after finish");
let prefix = parts.prefix.trim().trim_right_matches('/'); let prefix = parts.prefix.trim().trim_right_matches('/');
let (prefix, prefix_len) = if prefix.is_empty() { let (prefix, prefix_len) = if prefix.is_empty() {
("/".to_owned(), 0) ("/".to_owned(), 0)
@ -628,11 +628,19 @@ where
resources.push((pattern, None)); resources.push((pattern, None));
} }
for ref mut handler in parts.handlers.iter_mut() {
if let PrefixHandlerType::Scope(_, ref mut route_handler, _) = handler {
if !route_handler.has_default_resource() {
route_handler.default_resource(Rc::clone(&parts.default));
}
};
}
let (router, resources) = Router::new(&prefix, parts.settings, resources); let (router, resources) = Router::new(&prefix, parts.settings, resources);
let inner = Rc::new(RefCell::new(Inner { let inner = Rc::new(RefCell::new(Inner {
prefix: prefix_len, prefix: prefix_len,
default: parts.default, default: Rc::clone(&parts.default),
encoding: parts.encoding, encoding: parts.encoding,
handlers: parts.handlers, handlers: parts.handlers,
resources, resources,

View File

@ -1,5 +1,7 @@
use std::cell::RefCell;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::Deref; use std::ops::Deref;
use std::rc::Rc;
use futures::future::{err, ok, Future}; use futures::future::{err, ok, Future};
use futures::{Async, Poll}; use futures::{Async, Poll};
@ -8,6 +10,7 @@ use error::Error;
use http::StatusCode; use http::StatusCode;
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
use resource::ResourceHandler;
/// Trait defines object that could be registered as route handler /// Trait defines object that could be registered as route handler
#[allow(unused_variables)] #[allow(unused_variables)]
@ -403,6 +406,14 @@ where
// /// Trait defines object that could be registered as resource route // /// Trait defines object that could be registered as resource route
pub(crate) trait RouteHandler<S>: 'static { pub(crate) trait RouteHandler<S>: 'static {
fn handle(&mut self, req: HttpRequest<S>) -> AsyncResult<HttpResponse>; fn handle(&mut self, req: HttpRequest<S>) -> AsyncResult<HttpResponse>;
fn has_default_resource(&self) -> bool {
false
}
fn default_resource(&mut self, default: Rc<RefCell<ResourceHandler<S>>>) {
unimplemented!()
}
} }
/// Route handler wrapper for Handler /// Route handler wrapper for Handler

View File

@ -405,6 +405,14 @@ impl<S: 'static> RouteHandler<S> for Scope<S> {
unimplemented!() unimplemented!()
} }
} }
fn has_default_resource(&self) -> bool {
self.default.is_some()
}
fn default_resource(&mut self, default: ScopeResource<S>) {
self.default = Some(default);
}
} }
struct Wrapper<S: 'static> { struct Wrapper<S: 'static> {
@ -1188,4 +1196,27 @@ mod tests {
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
} }
#[test]
fn test_default_resource_propagation() {
let mut app = App::new()
.scope("/app1", |scope| {
scope.default_resource(|r| r.f(|_| HttpResponse::BadRequest()))
})
.scope("/app2", |scope| scope)
.default_resource(|r| r.f(|_| HttpResponse::MethodNotAllowed()))
.finish();
let req = TestRequest::with_uri("/non-exist").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::METHOD_NOT_ALLOWED);
let req = TestRequest::with_uri("/app1/non-exist").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::BAD_REQUEST);
let req = TestRequest::with_uri("/app2/non-exist").finish();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::METHOD_NOT_ALLOWED);
}
} }