1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-28 01:32:57 +01:00

allow to handle entire sub path

This commit is contained in:
Nikolay Kim 2018-01-02 13:09:02 -08:00
parent 284b59722a
commit 9040f588af

View File

@ -5,6 +5,7 @@ use std::collections::HashMap;
use handler::Reply; use handler::Reply;
use router::{Router, Pattern}; use router::{Router, Pattern};
use resource::Resource; use resource::Resource;
use handler::{Handler, RouteHandler, WrapHandler};
use httprequest::HttpRequest; use httprequest::HttpRequest;
use channel::{HttpHandler, IntoHttpHandler, HttpHandlerTask}; use channel::{HttpHandler, IntoHttpHandler, HttpHandlerTask};
use pipeline::{Pipeline, PipelineHandler}; use pipeline::{Pipeline, PipelineHandler};
@ -24,6 +25,7 @@ pub(crate) struct Inner<S> {
default: Resource<S>, default: Resource<S>,
router: Router, router: Router,
resources: Vec<Resource<S>>, resources: Vec<Resource<S>>,
handlers: Vec<(String, Box<RouteHandler<S>>)>,
} }
impl<S: 'static> PipelineHandler<S> for Inner<S> { impl<S: 'static> PipelineHandler<S> for Inner<S> {
@ -32,6 +34,17 @@ impl<S: 'static> PipelineHandler<S> for Inner<S> {
if let Some(idx) = self.router.recognize(&mut req) { if let Some(idx) = self.router.recognize(&mut req) {
self.resources[idx].handle(req.clone(), Some(&mut self.default)) self.resources[idx].handle(req.clone(), Some(&mut self.default))
} else { } else {
for &mut (ref prefix, ref mut handler) in &mut self.handlers {
let m = {
let path = req.path();
path.starts_with(prefix) && (
path.len() == prefix.len() ||
path.split_at(prefix.len()).1.starts_with('/'))
};
if m {
return handler.handle(req)
}
}
self.default.handle(req, None) self.default.handle(req, None)
} }
} }
@ -73,6 +86,7 @@ struct ApplicationParts<S> {
settings: ServerSettings, settings: ServerSettings,
default: Resource<S>, default: Resource<S>,
resources: HashMap<Pattern, Option<Resource<S>>>, resources: HashMap<Pattern, Option<Resource<S>>>,
handlers: Vec<(String, Box<RouteHandler<S>>)>,
external: HashMap<String, Pattern>, external: HashMap<String, Pattern>,
middlewares: Vec<Box<Middleware<S>>>, middlewares: Vec<Box<Middleware<S>>>,
} }
@ -94,6 +108,7 @@ impl Application<()> {
settings: ServerSettings::default(), settings: ServerSettings::default(),
default: Resource::default_not_found(), default: Resource::default_not_found(),
resources: HashMap::new(), resources: HashMap::new(),
handlers: Vec::new(),
external: HashMap::new(), external: HashMap::new(),
middlewares: Vec::new(), middlewares: Vec::new(),
}) })
@ -122,6 +137,7 @@ impl<S> Application<S> where S: 'static {
settings: ServerSettings::default(), settings: ServerSettings::default(),
default: Resource::default_not_found(), default: Resource::default_not_found(),
resources: HashMap::new(), resources: HashMap::new(),
handlers: Vec::new(),
external: HashMap::new(), external: HashMap::new(),
middlewares: Vec::new(), middlewares: Vec::new(),
}) })
@ -133,7 +149,7 @@ impl<S> Application<S> where S: 'static {
/// Only requests that matches application's prefix get processed by this application. /// Only requests that matches application's prefix get processed by this application.
/// Application prefix always contains laading "/" slash. If supplied prefix /// Application prefix always contains laading "/" slash. If supplied prefix
/// does not contain leading slash, it get inserted. Prefix should /// does not contain leading slash, it get inserted. Prefix should
/// consists of valud path segments. i.e for application with /// consists valid path segments. i.e for application with
/// prefix `/app` any request with following paths `/app`, `/app/` or `/app/test` /// prefix `/app` any request with following paths `/app`, `/app/` or `/app/test`
/// would match, but path `/application` would not match. /// would match, but path `/application` would not match.
/// ///
@ -194,8 +210,7 @@ impl<S> Application<S> where S: 'static {
/// .resource("/test", |r| { /// .resource("/test", |r| {
/// r.method(Method::GET).f(|_| httpcodes::HTTPOk); /// r.method(Method::GET).f(|_| httpcodes::HTTPOk);
/// r.method(Method::HEAD).f(|_| httpcodes::HTTPMethodNotAllowed); /// r.method(Method::HEAD).f(|_| httpcodes::HTTPMethodNotAllowed);
/// }) /// });
/// .finish();
/// } /// }
/// ``` /// ```
pub fn resource<F>(mut self, path: &str, f: F) -> Application<S> pub fn resource<F>(mut self, path: &str, f: F) -> Application<S>
@ -267,6 +282,36 @@ impl<S> Application<S> where S: 'static {
self self
} }
/// Configure handler for specific path prefix.
///
/// Path prefix consists valid path segments. i.e for prefix `/app`
/// any request with following paths `/app`, `/app/` or `/app/test`
/// would match, but path `/application` would not match.
///
/// ```rust
/// # extern crate actix_web;
/// use actix_web::*;
///
/// fn main() {
/// let app = Application::new()
/// .handler("/app", |req: HttpRequest| {
/// match *req.method() {
/// Method::GET => httpcodes::HTTPOk,
/// Method::POST => httpcodes::HTTPMethodNotAllowed,
/// _ => httpcodes::HTTPNotFound,
/// }});
/// }
/// ```
pub fn handler<H: Handler<S>>(mut self, path: &str, handler: H) -> Application<S>
{
{
let path = path.trim().trim_right_matches('/').to_owned();
let parts = self.parts.as_mut().expect("Use after finish");
parts.handlers.push((path, Box::new(WrapHandler::new(handler))));
}
self
}
/// Register a middleware /// Register a middleware
pub fn middleware<T>(mut self, mw: T) -> Application<S> pub fn middleware<T>(mut self, mw: T) -> Application<S>
where T: Middleware<S> + 'static where T: Middleware<S> + 'static
@ -292,7 +337,9 @@ impl<S> Application<S> where S: 'static {
Inner { Inner {
default: parts.default, default: parts.default,
router: router.clone(), router: router.clone(),
resources: resources } resources: resources,
handlers: parts.handlers,
}
)); ));
HttpApplication { HttpApplication {
@ -345,8 +392,7 @@ impl<S: 'static> Iterator for Application<S> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::str::FromStr; use http::StatusCode;
use http::{Method, Version, Uri, HeaderMap, StatusCode};
use super::*; use super::*;
use test::TestRequest; use test::TestRequest;
use httprequest::HttpRequest; use httprequest::HttpRequest;
@ -362,18 +408,14 @@ mod tests {
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_response().unwrap().status(), StatusCode::OK); assert_eq!(resp.as_response().unwrap().status(), StatusCode::OK);
let req = HttpRequest::new( let req = TestRequest::with_uri("/blah").finish();
Method::GET, Uri::from_str("/blah").unwrap(),
Version::HTTP_11, HeaderMap::new(), None);
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND);
let mut app = Application::new() let mut app = Application::new()
.default_resource(|r| r.h(httpcodes::HTTPMethodNotAllowed)) .default_resource(|r| r.h(httpcodes::HTTPMethodNotAllowed))
.finish(); .finish();
let req = HttpRequest::new( let req = TestRequest::with_uri("/blah").finish();
Method::GET, Uri::from_str("/blah").unwrap(),
Version::HTTP_11, HeaderMap::new(), None);
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_response().unwrap().status(), StatusCode::METHOD_NOT_ALLOWED); assert_eq!(resp.as_response().unwrap().status(), StatusCode::METHOD_NOT_ALLOWED);
} }
@ -411,8 +453,40 @@ mod tests {
let resp = app.handle(req); let resp = app.handle(req);
assert!(resp.is_ok()); assert!(resp.is_ok());
let req = TestRequest::with_uri("/test/blah").finish();
let resp = app.handle(req);
assert!(resp.is_ok());
let req = TestRequest::with_uri("/testing").finish(); let req = TestRequest::with_uri("/testing").finish();
let resp = app.handle(req); let resp = app.handle(req);
assert!(resp.is_err()); assert!(resp.is_err());
} }
#[test]
fn test_handler() {
let mut app = Application::new()
.handler("/test", httpcodes::HTTPOk)
.finish();
let req = TestRequest::with_uri("/test").finish();
let resp = app.run(req);
assert_eq!(resp.as_response().unwrap().status(), StatusCode::OK);
let req = TestRequest::with_uri("/test/").finish();
let resp = app.run(req);
assert_eq!(resp.as_response().unwrap().status(), StatusCode::OK);
let req = TestRequest::with_uri("/test/app").finish();
let resp = app.run(req);
assert_eq!(resp.as_response().unwrap().status(), StatusCode::OK);
let req = TestRequest::with_uri("/testapp").finish();
let resp = app.run(req);
assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/blah").finish();
let resp = app.run(req);
assert_eq!(resp.as_response().unwrap().status(), StatusCode::NOT_FOUND);
}
} }