1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-03-15 20:53:06 +01:00

add application filters

This commit is contained in:
Nikolay Kim 2018-06-07 19:46:38 -07:00
parent 40ff550460
commit e3cd0fdd13
2 changed files with 115 additions and 50 deletions

View File

@ -22,7 +22,8 @@ pub struct HttpApplication<S = ()> {
prefix_len: usize, prefix_len: usize,
router: Router, router: Router,
inner: Rc<UnsafeCell<Inner<S>>>, inner: Rc<UnsafeCell<Inner<S>>>,
middlewares: Rc<Vec<Box<Middleware<S>>>>, filters: Option<Vec<Box<Predicate<S>>>>,
middlewares: Rc<RefCell<Vec<Box<Middleware<S>>>>>,
} }
pub(crate) struct Inner<S> { pub(crate) struct Inner<S> {
@ -143,11 +144,21 @@ impl<S: 'static> HttpHandler for HttpApplication<S> {
|| path.split_at(self.prefix_len).1.starts_with('/')) || path.split_at(self.prefix_len).1.starts_with('/'))
}; };
if m { if m {
let mut req = req.with_state(Rc::clone(&self.state), self.router.clone()); let mut req2 =
let tp = self.get_handler(&mut req); req.clone_with_state(Rc::clone(&self.state), self.router.clone());
if let Some(ref filters) = self.filters {
for filter in filters {
if !filter.check(&mut req2) {
return Err(req);
}
}
}
let tp = self.get_handler(&mut req2);
let inner = Rc::clone(&self.inner); let inner = Rc::clone(&self.inner);
Ok(Box::new(Pipeline::new( Ok(Box::new(Pipeline::new(
req, req2,
Rc::clone(&self.middlewares), Rc::clone(&self.middlewares),
inner, inner,
tp, tp,
@ -168,6 +179,7 @@ struct ApplicationParts<S> {
external: HashMap<String, Resource>, external: HashMap<String, Resource>,
encoding: ContentEncoding, encoding: ContentEncoding,
middlewares: Vec<Box<Middleware<S>>>, middlewares: Vec<Box<Middleware<S>>>,
filters: Vec<Box<Predicate<S>>>,
} }
/// Structure that follows the builder pattern for building application /// Structure that follows the builder pattern for building application
@ -190,6 +202,7 @@ impl App<()> {
handlers: Vec::new(), handlers: Vec::new(),
external: HashMap::new(), external: HashMap::new(),
encoding: ContentEncoding::Auto, encoding: ContentEncoding::Auto,
filters: Vec::new(),
middlewares: Vec::new(), middlewares: Vec::new(),
}), }),
} }
@ -229,6 +242,7 @@ where
handlers: Vec::new(), handlers: Vec::new(),
external: HashMap::new(), external: HashMap::new(),
middlewares: Vec::new(), middlewares: Vec::new(),
filters: Vec::new(),
encoding: ContentEncoding::Auto, encoding: ContentEncoding::Auto,
}), }),
} }
@ -267,8 +281,8 @@ where
/// let app = App::new() /// let app = App::new()
/// .prefix("/app") /// .prefix("/app")
/// .resource("/test", |r| { /// .resource("/test", |r| {
/// r.get().f(|_| HttpResponse::Ok()); /// r.get().f(|_| HttpResponse::Ok());
/// r.head().f(|_| HttpResponse::MethodNotAllowed()); /// r.head().f(|_| HttpResponse::MethodNotAllowed());
/// }) /// })
/// .finish(); /// .finish();
/// } /// }
@ -285,6 +299,26 @@ where
self self
} }
/// Add match predicate to application.
///
/// ```rust
/// # extern crate actix_web;
/// # use actix_web::*;
/// # fn main() {
/// App::new()
/// .filter(pred::Get())
/// .resource("/path", |r| r.f(|_| HttpResponse::Ok()))
/// # .finish();
/// # }
/// ```
pub fn filter<T: Predicate<S> + 'static>(mut self, p: T) -> App<S> {
{
let parts = self.parts.as_mut().expect("Use after finish");
parts.filters.push(Box::new(p));
}
self
}
/// Configure route for a specific path. /// Configure route for a specific path.
/// ///
/// This is a simplified version of the `App::resource()` method. /// This is a simplified version of the `App::resource()` method.
@ -300,10 +334,12 @@ where
/// ///
/// fn main() { /// fn main() {
/// let app = App::new() /// let app = App::new()
/// .route("/test", http::Method::GET, /// .route("/test", http::Method::GET, |_: HttpRequest| {
/// |_: HttpRequest| HttpResponse::Ok()) /// HttpResponse::Ok()
/// .route("/test", http::Method::POST, /// })
/// |_: HttpRequest| HttpResponse::MethodNotAllowed()); /// .route("/test", http::Method::POST, |_: HttpRequest| {
/// HttpResponse::MethodNotAllowed()
/// });
/// } /// }
/// ``` /// ```
pub fn route<T, F, R>(mut self, path: &str, method: Method, f: F) -> App<S> pub fn route<T, F, R>(mut self, path: &str, method: Method, f: F) -> App<S>
@ -345,12 +381,12 @@ where
/// use actix_web::{http, App, HttpRequest, HttpResponse}; /// use actix_web::{http, App, HttpRequest, HttpResponse};
/// ///
/// fn main() { /// fn main() {
/// let app = App::new() /// let app = App::new().scope("/{project_id}", |scope| {
/// .scope("/{project_id}", |scope| { /// scope
/// scope.resource("/path1", |r| r.f(|_| HttpResponse::Ok())) /// .resource("/path1", |r| r.f(|_| HttpResponse::Ok()))
/// .resource("/path2", |r| r.f(|_| HttpResponse::Ok())) /// .resource("/path2", |r| r.f(|_| HttpResponse::Ok()))
/// .resource("/path3", |r| r.f(|_| HttpResponse::MethodNotAllowed())) /// .resource("/path3", |r| r.f(|_| HttpResponse::MethodNotAllowed()))
/// }); /// });
/// } /// }
/// ``` /// ```
/// ///
@ -402,11 +438,10 @@ where
/// use actix_web::{http, App, HttpResponse}; /// use actix_web::{http, App, HttpResponse};
/// ///
/// fn main() { /// fn main() {
/// let app = App::new() /// let app = App::new().resource("/users/{userid}/{friend}", |r| {
/// .resource("/users/{userid}/{friend}", |r| { /// r.get().f(|_| HttpResponse::Ok());
/// r.get().f(|_| HttpResponse::Ok()); /// r.head().f(|_| HttpResponse::MethodNotAllowed());
/// r.head().f(|_| HttpResponse::MethodNotAllowed()); /// });
/// });
/// } /// }
/// ``` /// ```
pub fn resource<F, R>(mut self, path: &str, f: F) -> App<S> pub fn resource<F, R>(mut self, path: &str, f: F) -> App<S>
@ -469,9 +504,9 @@ where
/// use actix_web::{App, HttpRequest, HttpResponse, Result}; /// use actix_web::{App, HttpRequest, HttpResponse, Result};
/// ///
/// fn index(mut req: HttpRequest) -> Result<HttpResponse> { /// fn index(mut req: HttpRequest) -> Result<HttpResponse> {
/// let url = req.url_for("youtube", &["oHg5SJYRHA0"])?; /// let url = req.url_for("youtube", &["oHg5SJYRHA0"])?;
/// assert_eq!(url.as_str(), "https://youtube.com/watch/oHg5SJYRHA0"); /// assert_eq!(url.as_str(), "https://youtube.com/watch/oHg5SJYRHA0");
/// Ok(HttpResponse::Ok().into()) /// Ok(HttpResponse::Ok().into())
/// } /// }
/// ///
/// fn main() { /// fn main() {
@ -514,13 +549,11 @@ where
/// use actix_web::{http, App, HttpRequest, HttpResponse}; /// use actix_web::{http, App, HttpRequest, HttpResponse};
/// ///
/// fn main() { /// fn main() {
/// let app = App::new() /// let app = App::new().handler("/app", |req: HttpRequest| match *req.method() {
/// .handler("/app", |req: HttpRequest| { /// http::Method::GET => HttpResponse::Ok(),
/// match *req.method() { /// http::Method::POST => HttpResponse::MethodNotAllowed(),
/// http::Method::GET => HttpResponse::Ok(), /// _ => HttpResponse::NotFound(),
/// http::Method::POST => HttpResponse::MethodNotAllowed(), /// });
/// _ => HttpResponse::NotFound(),
/// }});
/// } /// }
/// ``` /// ```
pub fn handler<H: Handler<S>>(mut self, path: &str, handler: H) -> App<S> { pub fn handler<H: Handler<S>>(mut self, path: &str, handler: H) -> App<S> {
@ -561,15 +594,14 @@ where
/// ///
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_web;
/// use actix_web::{App, HttpResponse, fs, middleware}; /// use actix_web::{fs, middleware, App, HttpResponse};
/// ///
/// // this function could be located in different module /// // this function could be located in different module
/// fn config(app: App) -> App { /// fn config(app: App) -> App {
/// app /// app.resource("/test", |r| {
/// .resource("/test", |r| { /// r.get().f(|_| HttpResponse::Ok());
/// r.get().f(|_| HttpResponse::Ok()); /// r.head().f(|_| HttpResponse::MethodNotAllowed());
/// r.head().f(|_| HttpResponse::MethodNotAllowed()); /// })
/// })
/// } /// }
/// ///
/// fn main() { /// fn main() {
@ -610,6 +642,11 @@ where
handlers: parts.handlers, handlers: parts.handlers,
resources, resources,
})); }));
let filters = if parts.filters.is_empty() {
None
} else {
Some(parts.filters)
};
HttpApplication { HttpApplication {
state: Rc::new(parts.state), state: Rc::new(parts.state),
@ -618,6 +655,7 @@ where
prefix, prefix,
prefix_len, prefix_len,
inner, inner,
filters,
} }
} }
@ -636,19 +674,22 @@ where
/// struct State2; /// struct State2;
/// ///
/// fn main() { /// fn main() {
/// # thread::spawn(|| { /// //#### # thread::spawn(|| {
/// server::new(|| { vec![ /// server::new(|| {
/// App::with_state(State1) /// vec![
/// .prefix("/app1") /// App::with_state(State1)
/// .resource("/", |r| r.f(|r| HttpResponse::Ok())) /// .prefix("/app1")
/// .boxed(), /// .resource("/", |r| r.f(|r| HttpResponse::Ok()))
/// App::with_state(State2) /// .boxed(),
/// .prefix("/app2") /// App::with_state(State2)
/// .resource("/", |r| r.f(|r| HttpResponse::Ok())) /// .prefix("/app2")
/// .boxed() ]}) /// .resource("/", |r| r.f(|r| HttpResponse::Ok()))
/// .bind("127.0.0.1:8080").unwrap() /// .boxed(),
/// ]
/// }).bind("127.0.0.1:8080")
/// .unwrap()
/// .run() /// .run()
/// # }); /// //#### # });
/// } /// }
/// ``` /// ```
pub fn boxed(mut self) -> Box<HttpHandler> { pub fn boxed(mut self) -> Box<HttpHandler> {
@ -699,7 +740,8 @@ mod tests {
use http::StatusCode; use http::StatusCode;
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
use test::TestRequest; use pred;
use test::{TestRequest, TestServer};
#[test] #[test]
fn test_default_resource() { fn test_default_resource() {
@ -898,4 +940,21 @@ 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_filter() {
let mut srv = TestServer::with_factory(|| {
App::new()
.filter(pred::Get())
.handler("/test", |_| HttpResponse::Ok())
});
let request = srv.get().uri(srv.url("/test")).finish().unwrap();
let response = srv.execute(request.send()).unwrap();
assert_eq!(response.status(), StatusCode::OK);
let request = srv.post().uri(srv.url("/test")).finish().unwrap();
let response = srv.execute(request.send()).unwrap();
assert_eq!(response.status(), StatusCode::NOT_FOUND);
}
} }

View File

@ -141,6 +141,12 @@ impl HttpRequest<()> {
pub fn with_state<S>(self, state: Rc<S>, router: Router) -> HttpRequest<S> { pub fn with_state<S>(self, state: Rc<S>, router: Router) -> HttpRequest<S> {
HttpRequest(self.0, Some(state), Some(router)) HttpRequest(self.0, Some(state), Some(router))
} }
pub(crate) fn clone_with_state<S>(
&self, state: Rc<S>, router: Router,
) -> HttpRequest<S> {
HttpRequest(self.0.clone(), Some(state), Some(router))
}
} }
impl<S> HttpMessage for HttpRequest<S> { impl<S> HttpMessage for HttpRequest<S> {