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

allow to specify multi pattern for resources

This commit is contained in:
Nikolay Kim 2019-12-25 20:13:52 +04:00
parent 7882f545e5
commit f86ce0390e
16 changed files with 54 additions and 33 deletions

View File

@ -8,6 +8,7 @@
* Allow to gracefully stop test server via `TestServer::stop()` * Allow to gracefully stop test server via `TestServer::stop()`
* Allow to specify multi-patterns for resources
## [2.0.0-rc] - 2019-12-20 ## [2.0.0-rc] - 2019-12-20

View File

@ -62,7 +62,7 @@ rustls = ["actix-tls/rustls", "awc/rustls", "rust-tls"]
actix-codec = "0.2.0" actix-codec = "0.2.0"
actix-service = "1.0.1" actix-service = "1.0.1"
actix-utils = "1.0.4" actix-utils = "1.0.4"
actix-router = "0.2.0" actix-router = "0.2.1"
actix-rt = "1.0.0" actix-rt = "1.0.0"
actix-server = "1.0.0" actix-server = "1.0.0"
actix-testing = "1.0.0" actix-testing = "1.0.0"
@ -115,4 +115,4 @@ actix-identity = { path = "actix-identity" }
actix-session = { path = "actix-session" } actix-session = { path = "actix-session" }
actix-files = { path = "actix-files" } actix-files = { path = "actix-files" }
actix-multipart = { path = "actix-multipart" } actix-multipart = { path = "actix-multipart" }
awc = { path = "awc" } awc = { path = "awc" }

View File

@ -814,7 +814,7 @@ where
res res
} }
} }
.boxed_local(), .boxed_local(),
) )
} }
} }

View File

@ -66,7 +66,7 @@ where
service service
}) })
} }
.boxed_local() .boxed_local()
} }
} }

View File

@ -154,6 +154,6 @@ where
} }
Ok(()) Ok(())
} }
.boxed_local() .boxed_local()
} }
} }

View File

@ -294,7 +294,7 @@ where
Err(err) => Ok(req.error_response(err)), Err(err) => Ok(req.error_response(err)),
} }
} }
.boxed_local() .boxed_local()
} }
} }

View File

@ -354,7 +354,7 @@ where
} }
}) })
} }
.boxed_local() .boxed_local()
} }
} }

View File

@ -151,12 +151,13 @@ pub mod dev {
pub use actix_server::Server; pub use actix_server::Server;
pub use actix_service::{Service, Transform}; pub use actix_service::{Service, Transform};
pub(crate) fn insert_slash(path: &str) -> String { pub(crate) fn insert_slash(mut patterns: Vec<String>) -> Vec<String> {
let mut path = path.to_owned(); for path in &mut patterns {
if !path.is_empty() && !path.starts_with('/') { if !path.is_empty() && !path.starts_with('/') {
path.insert(0, '/'); path.insert(0, '/');
}; };
path }
patterns
} }
use crate::http::header::ContentEncoding; use crate::http::header::ContentEncoding;

View File

@ -150,7 +150,7 @@ where
} }
Ok(res) Ok(res)
} }
.boxed_local() .boxed_local()
} }
} }

View File

@ -140,7 +140,7 @@ where
Ok(res) Ok(res)
} }
} }
.boxed_local() .boxed_local()
} }
} }

View File

@ -6,6 +6,7 @@ use std::rc::Rc;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use actix_http::{Error, Extensions, Response}; use actix_http::{Error, Extensions, Response};
use actix_router::IntoPattern;
use actix_service::boxed::{self, BoxService, BoxServiceFactory}; use actix_service::boxed::{self, BoxService, BoxServiceFactory};
use actix_service::{ use actix_service::{
apply, apply_fn_factory, IntoServiceFactory, Service, ServiceFactory, Transform, apply, apply_fn_factory, IntoServiceFactory, Service, ServiceFactory, Transform,
@ -48,7 +49,7 @@ type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Err
/// Default behavior could be overriden with `default_resource()` method. /// Default behavior could be overriden with `default_resource()` method.
pub struct Resource<T = ResourceEndpoint> { pub struct Resource<T = ResourceEndpoint> {
endpoint: T, endpoint: T,
rdef: String, rdef: Vec<String>,
name: Option<String>, name: Option<String>,
routes: Vec<Route>, routes: Vec<Route>,
data: Option<Extensions>, data: Option<Extensions>,
@ -58,12 +59,12 @@ pub struct Resource<T = ResourceEndpoint> {
} }
impl Resource { impl Resource {
pub fn new(path: &str) -> Resource { pub fn new<T: IntoPattern>(path: T) -> Resource {
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(), rdef: path.patterns(),
name: None, name: None,
endpoint: ResourceEndpoint::new(fref.clone()), endpoint: ResourceEndpoint::new(fref.clone()),
factory_ref: fref, factory_ref: fref,
@ -381,9 +382,9 @@ where
Some(std::mem::replace(&mut self.guards, Vec::new())) Some(std::mem::replace(&mut self.guards, Vec::new()))
}; };
let mut rdef = if config.is_root() || !self.rdef.is_empty() { let mut rdef = if config.is_root() || !self.rdef.is_empty() {
ResourceDef::new(&insert_slash(&self.rdef)) ResourceDef::new(insert_slash(self.rdef.clone()))
} else { } else {
ResourceDef::new(&self.rdef) ResourceDef::new(self.rdef.clone())
}; };
if let Some(ref name) = self.name { if let Some(ref name) = self.name {
*rdef.name_mut() = name.clone(); *rdef.name_mut() = name.clone();
@ -660,6 +661,23 @@ mod tests {
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
} }
#[actix_rt::test]
async fn test_pattern() {
let mut srv =
init_service(App::new().service(web::resource(["/test", "/test2"]).to(|| {
async {
Ok::<_, Error>(HttpResponse::Ok())
}
})))
.await;
let req = TestRequest::with_uri("/test").to_request();
let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK);
let req = TestRequest::with_uri("/test2").to_request();
let resp = call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK);
}
#[actix_rt::test] #[actix_rt::test]
async fn test_default_resource() { async fn test_default_resource() {
let mut srv = init_service( let mut srv = init_service(

View File

@ -8,7 +8,7 @@ use actix_http::{
Error, Extensions, HttpMessage, Payload, PayloadStream, RequestHead, Response, Error, Extensions, HttpMessage, Payload, PayloadStream, RequestHead, Response,
ResponseHead, ResponseHead,
}; };
use actix_router::{Path, Resource, ResourceDef, Url}; use actix_router::{IntoPattern, Path, Resource, ResourceDef, Url};
use actix_service::{IntoServiceFactory, ServiceFactory}; use actix_service::{IntoServiceFactory, ServiceFactory};
use crate::config::{AppConfig, AppService}; use crate::config::{AppConfig, AppService};
@ -422,16 +422,16 @@ impl<B: MessageBody> fmt::Debug for ServiceResponse<B> {
} }
pub struct WebService { pub struct WebService {
rdef: String, rdef: Vec<String>,
name: Option<String>, name: Option<String>,
guards: Vec<Box<dyn Guard>>, guards: Vec<Box<dyn Guard>>,
} }
impl WebService { impl WebService {
/// Create new `WebService` instance. /// Create new `WebService` instance.
pub fn new(path: &str) -> Self { pub fn new<T: IntoPattern>(path: T) -> Self {
WebService { WebService {
rdef: path.to_string(), rdef: path.patterns(),
name: None, name: None,
guards: Vec::new(), guards: Vec::new(),
} }
@ -491,7 +491,7 @@ impl WebService {
struct WebServiceImpl<T> { struct WebServiceImpl<T> {
srv: T, srv: T,
rdef: String, rdef: Vec<String>,
name: Option<String>, name: Option<String>,
guards: Vec<Box<dyn Guard>>, guards: Vec<Box<dyn Guard>>,
} }
@ -514,9 +514,9 @@ where
}; };
let mut rdef = if config.is_root() || !self.rdef.is_empty() { let mut rdef = if config.is_root() || !self.rdef.is_empty() {
ResourceDef::new(&insert_slash(&self.rdef)) ResourceDef::new(insert_slash(self.rdef))
} else { } else {
ResourceDef::new(&self.rdef) ResourceDef::new(self.rdef)
}; };
if let Some(ref name) = self.name { if let Some(ref name) = self.name {
*rdef.name_mut() = name.clone(); *rdef.name_mut() = name.clone();

View File

@ -365,7 +365,7 @@ where
.map_err(|_| UrlencodedError::Parse) .map_err(|_| UrlencodedError::Parse)
} }
} }
.boxed_local(), .boxed_local(),
); );
self.poll(cx) self.poll(cx)
} }

View File

@ -396,7 +396,7 @@ where
} }
Ok(serde_json::from_slice::<U>(&body)?) Ok(serde_json::from_slice::<U>(&body)?)
} }
.boxed_local(), .boxed_local(),
); );
self.poll(cx) self.poll(cx)

View File

@ -229,7 +229,7 @@ impl FromRequest for String {
.ok_or_else(|| ErrorBadRequest("Can not decode body"))?) .ok_or_else(|| ErrorBadRequest("Can not decode body"))?)
} }
} }
.boxed_local(), .boxed_local(),
) )
} }
} }
@ -391,7 +391,7 @@ impl Future for HttpMessageBody {
} }
Ok(body.freeze()) Ok(body.freeze())
} }
.boxed_local(), .boxed_local(),
); );
self.poll(cx) self.poll(cx)
} }

View File

@ -1,5 +1,6 @@
//! Essentials helper functions and types for application registration. //! Essentials helper functions and types for application registration.
use actix_http::http::Method; use actix_http::http::Method;
use actix_router::IntoPattern;
use futures::Future; use futures::Future;
pub use actix_http::Response as HttpResponse; pub use actix_http::Response as HttpResponse;
@ -50,7 +51,7 @@ pub use crate::types::*;
/// .route(web::head().to(|| HttpResponse::MethodNotAllowed())) /// .route(web::head().to(|| HttpResponse::MethodNotAllowed()))
/// ); /// );
/// ``` /// ```
pub fn resource(path: &str) -> Resource { pub fn resource<T: IntoPattern>(path: T) -> Resource {
Resource::new(path) Resource::new(path)
} }
@ -249,7 +250,7 @@ where
/// .finish(my_service) /// .finish(my_service)
/// ); /// );
/// ``` /// ```
pub fn service(path: &str) -> WebService { pub fn service<T: IntoPattern>(path: T) -> WebService {
WebService::new(path) WebService::new(path)
} }