mirror of
https://github.com/actix/actix-web.git
synced 2025-01-01 21:08:43 +00:00
allow scope level guards
This commit is contained in:
parent
5c61321565
commit
e442ddb167
4 changed files with 179 additions and 107 deletions
|
@ -49,7 +49,6 @@ actix-utils = "0.3.0"
|
|||
|
||||
actix-http = { git = "https://github.com/actix/actix-http.git" }
|
||||
actix-router = { git = "https://github.com/actix/actix-net.git" }
|
||||
#actix-router = { path = "../actix-net/router" }
|
||||
|
||||
bytes = "0.4"
|
||||
derive_more = "0.14"
|
||||
|
|
86
src/app.rs
86
src/app.rs
|
@ -13,11 +13,13 @@ use actix_service::{
|
|||
use futures::future::{ok, Either, FutureResult};
|
||||
use futures::{Async, Future, IntoFuture, Poll};
|
||||
|
||||
use crate::guard::Guard;
|
||||
use crate::resource::Resource;
|
||||
use crate::scope::{insert_slash, Scope};
|
||||
use crate::service::{ServiceRequest, ServiceResponse};
|
||||
use crate::state::{State, StateFactory, StateFactoryResult};
|
||||
|
||||
type Guards = Vec<Box<Guard>>;
|
||||
type HttpService<P> = BoxedService<ServiceRequest<P>, ServiceResponse, ()>;
|
||||
type HttpNewService<P> = BoxedNewService<(), ServiceRequest<P>, ServiceResponse, (), ()>;
|
||||
type BoxedResponse = Box<Future<Item = ServiceResponse, Error = ()>>;
|
||||
|
@ -141,14 +143,15 @@ where
|
|||
where
|
||||
F: FnOnce(Scope<P>) -> Scope<P>,
|
||||
{
|
||||
let scope = f(Scope::new(path));
|
||||
let mut scope = f(Scope::new(path));
|
||||
let rdef = scope.rdef().clone();
|
||||
let default = scope.get_default();
|
||||
let guards = scope.take_guards();
|
||||
|
||||
let fref = Rc::new(RefCell::new(None));
|
||||
AppRouter {
|
||||
chain: self.chain,
|
||||
services: vec![(rdef, boxed::new_service(scope.into_new_service()))],
|
||||
services: vec![(rdef, boxed::new_service(scope.into_new_service()), guards)],
|
||||
default: None,
|
||||
defaults: vec![default],
|
||||
endpoint: AppEntry::new(fref.clone()),
|
||||
|
@ -201,13 +204,13 @@ where
|
|||
> + 'static,
|
||||
{
|
||||
let rdef = ResourceDef::new(&insert_slash(path));
|
||||
let resource = f(Resource::new());
|
||||
let default = resource.get_default();
|
||||
let res = f(Resource::new());
|
||||
let default = res.get_default();
|
||||
|
||||
let fref = Rc::new(RefCell::new(None));
|
||||
AppRouter {
|
||||
chain: self.chain,
|
||||
services: vec![(rdef, boxed::new_service(resource.into_new_service()))],
|
||||
services: vec![(rdef, boxed::new_service(res.into_new_service()), None)],
|
||||
default: None,
|
||||
defaults: vec![default],
|
||||
endpoint: AppEntry::new(fref.clone()),
|
||||
|
@ -308,7 +311,7 @@ where
|
|||
/// for building application instances.
|
||||
pub struct AppRouter<C, P, B, T> {
|
||||
chain: C,
|
||||
services: Vec<(ResourceDef, HttpNewService<P>)>,
|
||||
services: Vec<(ResourceDef, HttpNewService<P>, Option<Guards>)>,
|
||||
default: Option<Rc<HttpNewService<P>>>,
|
||||
defaults: Vec<Rc<RefCell<Option<Rc<HttpNewService<P>>>>>>,
|
||||
endpoint: T,
|
||||
|
@ -357,11 +360,12 @@ where
|
|||
where
|
||||
F: FnOnce(Scope<P>) -> Scope<P>,
|
||||
{
|
||||
let scope = f(Scope::new(path));
|
||||
let mut scope = f(Scope::new(path));
|
||||
let rdef = scope.rdef().clone();
|
||||
let guards = scope.take_guards();
|
||||
self.defaults.push(scope.get_default());
|
||||
self.services
|
||||
.push((rdef, boxed::new_service(scope.into_new_service())));
|
||||
.push((rdef, boxed::new_service(scope.into_new_service()), guards));
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -411,8 +415,11 @@ where
|
|||
let rdef = ResourceDef::new(&insert_slash(path));
|
||||
let resource = f(Resource::new());
|
||||
self.defaults.push(resource.get_default());
|
||||
self.services
|
||||
.push((rdef, boxed::new_service(resource.into_new_service())));
|
||||
self.services.push((
|
||||
rdef,
|
||||
boxed::new_service(resource.into_new_service()),
|
||||
None,
|
||||
));
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -452,6 +459,7 @@ where
|
|||
self.services.push((
|
||||
rdef.into(),
|
||||
boxed::new_service(factory.into_new_service().map_init_err(|_| ())),
|
||||
None,
|
||||
));
|
||||
self
|
||||
}
|
||||
|
@ -562,7 +570,12 @@ where
|
|||
// set factory
|
||||
*self.factory_ref.borrow_mut() = Some(AppRoutingFactory {
|
||||
default: self.default.clone(),
|
||||
services: Rc::new(self.services),
|
||||
services: Rc::new(
|
||||
self.services
|
||||
.into_iter()
|
||||
.map(|(rdef, srv, guards)| (rdef, srv, RefCell::new(guards)))
|
||||
.collect(),
|
||||
),
|
||||
});
|
||||
|
||||
AppInit {
|
||||
|
@ -575,7 +588,7 @@ where
|
|||
}
|
||||
|
||||
pub struct AppRoutingFactory<P> {
|
||||
services: Rc<Vec<(ResourceDef, HttpNewService<P>)>>,
|
||||
services: Rc<Vec<(ResourceDef, HttpNewService<P>, RefCell<Option<Guards>>)>>,
|
||||
default: Option<Rc<HttpNewService<P>>>,
|
||||
}
|
||||
|
||||
|
@ -598,9 +611,10 @@ impl<P: 'static> NewService for AppRoutingFactory<P> {
|
|||
fut: self
|
||||
.services
|
||||
.iter()
|
||||
.map(|(path, service)| {
|
||||
.map(|(path, service, guards)| {
|
||||
CreateAppRoutingItem::Future(
|
||||
Some(path.clone()),
|
||||
guards.borrow_mut().take(),
|
||||
service.new_service(&()),
|
||||
)
|
||||
})
|
||||
|
@ -622,8 +636,8 @@ pub struct AppRoutingFactoryResponse<P> {
|
|||
}
|
||||
|
||||
enum CreateAppRoutingItem<P> {
|
||||
Future(Option<ResourceDef>, HttpServiceFut<P>),
|
||||
Service(ResourceDef, HttpService<P>),
|
||||
Future(Option<ResourceDef>, Option<Guards>, HttpServiceFut<P>),
|
||||
Service(ResourceDef, Option<Guards>, HttpService<P>),
|
||||
}
|
||||
|
||||
impl<P> Future for AppRoutingFactoryResponse<P> {
|
||||
|
@ -643,20 +657,24 @@ impl<P> Future for AppRoutingFactoryResponse<P> {
|
|||
// poll http services
|
||||
for item in &mut self.fut {
|
||||
let res = match item {
|
||||
CreateAppRoutingItem::Future(ref mut path, ref mut fut) => {
|
||||
match fut.poll()? {
|
||||
Async::Ready(service) => Some((path.take().unwrap(), service)),
|
||||
CreateAppRoutingItem::Future(
|
||||
ref mut path,
|
||||
ref mut guards,
|
||||
ref mut fut,
|
||||
) => match fut.poll()? {
|
||||
Async::Ready(service) => {
|
||||
Some((path.take().unwrap(), guards.take(), service))
|
||||
}
|
||||
Async::NotReady => {
|
||||
done = false;
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
CreateAppRoutingItem::Service(_, _) => continue,
|
||||
},
|
||||
CreateAppRoutingItem::Service(_, _, _) => continue,
|
||||
};
|
||||
|
||||
if let Some((path, service)) = res {
|
||||
*item = CreateAppRoutingItem::Service(path, service);
|
||||
if let Some((path, guards, service)) = res {
|
||||
*item = CreateAppRoutingItem::Service(path, guards, service);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -666,10 +684,11 @@ impl<P> Future for AppRoutingFactoryResponse<P> {
|
|||
.drain(..)
|
||||
.fold(Router::build(), |mut router, item| {
|
||||
match item {
|
||||
CreateAppRoutingItem::Service(path, service) => {
|
||||
router.rdef(path, service)
|
||||
CreateAppRoutingItem::Service(path, guards, service) => {
|
||||
router.rdef(path, service);
|
||||
router.set_user_data(guards);
|
||||
}
|
||||
CreateAppRoutingItem::Future(_, _) => unreachable!(),
|
||||
CreateAppRoutingItem::Future(_, _, _) => unreachable!(),
|
||||
}
|
||||
router
|
||||
});
|
||||
|
@ -685,7 +704,7 @@ impl<P> Future for AppRoutingFactoryResponse<P> {
|
|||
}
|
||||
|
||||
pub struct AppRouting<P> {
|
||||
router: Router<HttpService<P>>,
|
||||
router: Router<HttpService<P>, Guards>,
|
||||
ready: Option<(ServiceRequest<P>, ResourceInfo)>,
|
||||
default: Option<HttpService<P>>,
|
||||
}
|
||||
|
@ -705,7 +724,18 @@ impl<P> Service for AppRouting<P> {
|
|||
}
|
||||
|
||||
fn call(&mut self, mut req: ServiceRequest<P>) -> Self::Future {
|
||||
if let Some((srv, _info)) = self.router.recognize_mut(req.match_info_mut()) {
|
||||
let res = self.router.recognize_mut_checked(&mut req, |req, guards| {
|
||||
if let Some(ref guards) = guards {
|
||||
for f in guards {
|
||||
if !f.check(req.head()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
});
|
||||
|
||||
if let Some((srv, _info)) = res {
|
||||
Either::A(srv.call(req))
|
||||
} else if let Some(ref mut default) = self.default {
|
||||
Either::A(default.call(req))
|
||||
|
|
179
src/scope.rs
179
src/scope.rs
|
@ -15,6 +15,7 @@ use crate::resource::Resource;
|
|||
use crate::route::Route;
|
||||
use crate::service::{ServiceRequest, ServiceResponse};
|
||||
|
||||
type Guards = Vec<Box<Guard>>;
|
||||
type HttpService<P> = BoxedService<ServiceRequest<P>, ServiceResponse, ()>;
|
||||
type HttpNewService<P> = BoxedNewService<(), ServiceRequest<P>, ServiceResponse, (), ()>;
|
||||
type BoxedResponse = Box<Future<Item = ServiceResponse, Error = ()>>;
|
||||
|
@ -51,8 +52,8 @@ type BoxedResponse = Box<Future<Item = ServiceResponse, Error = ()>>;
|
|||
pub struct Scope<P, T = ScopeEndpoint<P>> {
|
||||
endpoint: T,
|
||||
rdef: ResourceDef,
|
||||
services: Vec<(ResourceDef, HttpNewService<P>)>,
|
||||
guards: Rc<Vec<Box<Guard>>>,
|
||||
services: Vec<(ResourceDef, HttpNewService<P>, Option<Guards>)>,
|
||||
guards: Vec<Box<Guard>>,
|
||||
default: Rc<RefCell<Option<Rc<HttpNewService<P>>>>>,
|
||||
defaults: Vec<Rc<RefCell<Option<Rc<HttpNewService<P>>>>>>,
|
||||
factory_ref: Rc<RefCell<Option<ScopeFactory<P>>>>,
|
||||
|
@ -66,7 +67,7 @@ impl<P: 'static> Scope<P> {
|
|||
Scope {
|
||||
endpoint: ScopeEndpoint::new(fref.clone()),
|
||||
rdef: rdef.clone(),
|
||||
guards: Rc::new(Vec::new()),
|
||||
guards: Vec::new(),
|
||||
services: Vec::new(),
|
||||
default: Rc::new(RefCell::new(None)),
|
||||
defaults: Vec::new(),
|
||||
|
@ -110,7 +111,7 @@ where
|
|||
/// }
|
||||
/// ```
|
||||
pub fn guard<G: Guard + 'static>(mut self, guard: G) -> Self {
|
||||
Rc::get_mut(&mut self.guards).unwrap().push(Box::new(guard));
|
||||
self.guards.push(Box::new(guard));
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -135,11 +136,12 @@ where
|
|||
where
|
||||
F: FnOnce(Scope<P>) -> Scope<P>,
|
||||
{
|
||||
let scope = f(Scope::new(path));
|
||||
let mut scope = f(Scope::new(path));
|
||||
let rdef = scope.rdef().clone();
|
||||
let guards = scope.take_guards();
|
||||
self.defaults.push(scope.get_default());
|
||||
self.services
|
||||
.push((rdef, boxed::new_service(scope.into_new_service())));
|
||||
.push((rdef, boxed::new_service(scope.into_new_service()), guards));
|
||||
|
||||
self
|
||||
}
|
||||
|
@ -204,8 +206,11 @@ where
|
|||
let rdef = ResourceDef::new(&insert_slash(path));
|
||||
let resource = f(Resource::new());
|
||||
self.defaults.push(resource.get_default());
|
||||
self.services
|
||||
.push((rdef, boxed::new_service(resource.into_new_service())));
|
||||
self.services.push((
|
||||
rdef,
|
||||
boxed::new_service(resource.into_new_service()),
|
||||
None,
|
||||
));
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -270,6 +275,14 @@ where
|
|||
pub(crate) fn get_default(&self) -> Rc<RefCell<Option<Rc<HttpNewService<P>>>>> {
|
||||
self.default.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn take_guards(&mut self) -> Option<Vec<Box<Guard>>> {
|
||||
if self.guards.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(std::mem::replace(&mut self.guards, Vec::new()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn insert_slash(path: &str) -> String {
|
||||
|
@ -301,7 +314,12 @@ where
|
|||
|
||||
*self.factory_ref.borrow_mut() = Some(ScopeFactory {
|
||||
default: self.default.clone(),
|
||||
services: Rc::new(self.services),
|
||||
services: Rc::new(
|
||||
self.services
|
||||
.into_iter()
|
||||
.map(|(rdef, srv, guards)| (rdef, srv, RefCell::new(guards)))
|
||||
.collect(),
|
||||
),
|
||||
});
|
||||
|
||||
self.endpoint
|
||||
|
@ -309,7 +327,7 @@ where
|
|||
}
|
||||
|
||||
pub struct ScopeFactory<P> {
|
||||
services: Rc<Vec<(ResourceDef, HttpNewService<P>)>>,
|
||||
services: Rc<Vec<(ResourceDef, HttpNewService<P>, RefCell<Option<Guards>>)>>,
|
||||
default: Rc<RefCell<Option<Rc<HttpNewService<P>>>>>,
|
||||
}
|
||||
|
||||
|
@ -332,9 +350,10 @@ impl<P: 'static> NewService for ScopeFactory<P> {
|
|||
fut: self
|
||||
.services
|
||||
.iter()
|
||||
.map(|(path, service)| {
|
||||
.map(|(path, service, guards)| {
|
||||
CreateScopeServiceItem::Future(
|
||||
Some(path.clone()),
|
||||
guards.borrow_mut().take(),
|
||||
service.new_service(&()),
|
||||
)
|
||||
})
|
||||
|
@ -356,8 +375,8 @@ pub struct ScopeFactoryResponse<P> {
|
|||
type HttpServiceFut<P> = Box<Future<Item = HttpService<P>, Error = ()>>;
|
||||
|
||||
enum CreateScopeServiceItem<P> {
|
||||
Future(Option<ResourceDef>, HttpServiceFut<P>),
|
||||
Service(ResourceDef, HttpService<P>),
|
||||
Future(Option<ResourceDef>, Option<Guards>, HttpServiceFut<P>),
|
||||
Service(ResourceDef, Option<Guards>, HttpService<P>),
|
||||
}
|
||||
|
||||
impl<P> Future for ScopeFactoryResponse<P> {
|
||||
|
@ -377,20 +396,24 @@ impl<P> Future for ScopeFactoryResponse<P> {
|
|||
// poll http services
|
||||
for item in &mut self.fut {
|
||||
let res = match item {
|
||||
CreateScopeServiceItem::Future(ref mut path, ref mut fut) => {
|
||||
match fut.poll()? {
|
||||
Async::Ready(service) => Some((path.take().unwrap(), service)),
|
||||
CreateScopeServiceItem::Future(
|
||||
ref mut path,
|
||||
ref mut guards,
|
||||
ref mut fut,
|
||||
) => match fut.poll()? {
|
||||
Async::Ready(service) => {
|
||||
Some((path.take().unwrap(), guards.take(), service))
|
||||
}
|
||||
Async::NotReady => {
|
||||
done = false;
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
CreateScopeServiceItem::Service(_, _) => continue,
|
||||
},
|
||||
CreateScopeServiceItem::Service(_, _, _) => continue,
|
||||
};
|
||||
|
||||
if let Some((path, service)) = res {
|
||||
*item = CreateScopeServiceItem::Service(path, service);
|
||||
if let Some((path, guards, service)) = res {
|
||||
*item = CreateScopeServiceItem::Service(path, guards, service);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -400,10 +423,11 @@ impl<P> Future for ScopeFactoryResponse<P> {
|
|||
.drain(..)
|
||||
.fold(Router::build(), |mut router, item| {
|
||||
match item {
|
||||
CreateScopeServiceItem::Service(path, service) => {
|
||||
router.rdef(path, service)
|
||||
CreateScopeServiceItem::Service(path, guards, service) => {
|
||||
router.rdef(path, service);
|
||||
router.set_user_data(guards);
|
||||
}
|
||||
CreateScopeServiceItem::Future(_, _) => unreachable!(),
|
||||
CreateScopeServiceItem::Future(_, _, _) => unreachable!(),
|
||||
}
|
||||
router
|
||||
});
|
||||
|
@ -419,7 +443,7 @@ impl<P> Future for ScopeFactoryResponse<P> {
|
|||
}
|
||||
|
||||
pub struct ScopeService<P> {
|
||||
router: Router<HttpService<P>>,
|
||||
router: Router<HttpService<P>, Vec<Box<Guard>>>,
|
||||
default: Option<HttpService<P>>,
|
||||
_ready: Option<(ServiceRequest<P>, ResourceInfo)>,
|
||||
}
|
||||
|
@ -435,7 +459,18 @@ impl<P> Service for ScopeService<P> {
|
|||
}
|
||||
|
||||
fn call(&mut self, mut req: ServiceRequest<P>) -> Self::Future {
|
||||
if let Some((srv, _info)) = self.router.recognize_mut(req.match_info_mut()) {
|
||||
let res = self.router.recognize_mut_checked(&mut req, |req, guards| {
|
||||
if let Some(ref guards) = guards {
|
||||
for f in guards {
|
||||
if !f.check(req.head()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
});
|
||||
|
||||
if let Some((srv, _info)) = res {
|
||||
Either::A(srv.call(req))
|
||||
} else if let Some(ref mut default) = self.default {
|
||||
Either::A(default.call(req))
|
||||
|
@ -478,7 +513,7 @@ mod tests {
|
|||
use bytes::Bytes;
|
||||
|
||||
use crate::test::TestRequest;
|
||||
use crate::{web, App, HttpRequest, HttpResponse};
|
||||
use crate::{guard, web, App, HttpRequest, HttpResponse};
|
||||
|
||||
#[test]
|
||||
fn test_scope() {
|
||||
|
@ -614,30 +649,30 @@ mod tests {
|
|||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn test_scope_guard() {
|
||||
// let mut rt = actix_rt::Runtime::new().unwrap();
|
||||
// let app = App::new()
|
||||
// .scope("/app", |scope| {
|
||||
// scope
|
||||
// .guard(guard::Get())
|
||||
// .resource("/path1", |r| r.to(|| HttpResponse::Ok()))
|
||||
// })
|
||||
// .into_new_service();
|
||||
// let mut srv = rt.block_on(app.new_service(&())).unwrap();
|
||||
#[test]
|
||||
fn test_scope_guard() {
|
||||
let mut rt = actix_rt::Runtime::new().unwrap();
|
||||
let app = App::new()
|
||||
.scope("/app", |scope| {
|
||||
scope
|
||||
.guard(guard::Get())
|
||||
.resource("/path1", |r| r.to(|| HttpResponse::Ok()))
|
||||
})
|
||||
.into_new_service();
|
||||
let mut srv = rt.block_on(app.new_service(&())).unwrap();
|
||||
|
||||
// let req = TestRequest::with_uri("/app/path1")
|
||||
// .method(Method::POST)
|
||||
// .to_request();
|
||||
// let resp = rt.block_on(srv.call(req)).unwrap();
|
||||
// assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||
let req = TestRequest::with_uri("/app/path1")
|
||||
.method(Method::POST)
|
||||
.to_request();
|
||||
let resp = rt.block_on(srv.call(req)).unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||
|
||||
// let req = TestRequest::with_uri("/app/path1")
|
||||
// .method(Method::GET)
|
||||
// .to_request();
|
||||
// let resp = rt.block_on(srv.call(req)).unwrap();
|
||||
// assert_eq!(resp.status(), StatusCode::OK);
|
||||
// }
|
||||
let req = TestRequest::with_uri("/app/path1")
|
||||
.method(Method::GET)
|
||||
.to_request();
|
||||
let resp = rt.block_on(srv.call(req)).unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_variable_segment() {
|
||||
|
@ -728,30 +763,32 @@ mod tests {
|
|||
assert_eq!(resp.status(), StatusCode::CREATED);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn test_nested_scope_filter() {
|
||||
// let app = App::new()
|
||||
// .scope("/app", |scope| {
|
||||
// scope.nested("/t1", |scope| {
|
||||
// scope
|
||||
// .filter(pred::Get())
|
||||
// .resource("/path1", |r| r.f(|_| HttpResponse::Ok()))
|
||||
// })
|
||||
// })
|
||||
// .finish();
|
||||
#[test]
|
||||
fn test_nested_scope_filter() {
|
||||
let mut rt = actix_rt::Runtime::new().unwrap();
|
||||
let app = App::new()
|
||||
.scope("/app", |scope| {
|
||||
scope.nested("/t1", |scope| {
|
||||
scope
|
||||
.guard(guard::Get())
|
||||
.resource("/path1", |r| r.to(|| HttpResponse::Ok()))
|
||||
})
|
||||
})
|
||||
.into_new_service();
|
||||
let mut srv = rt.block_on(app.new_service(&())).unwrap();
|
||||
|
||||
// let req = TestRequest::with_uri("/app/t1/path1")
|
||||
// .method(Method::POST)
|
||||
// .request();
|
||||
// let resp = app.run(req);
|
||||
// assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
let req = TestRequest::with_uri("/app/t1/path1")
|
||||
.method(Method::POST)
|
||||
.to_request();
|
||||
let resp = rt.block_on(srv.call(req)).unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||
|
||||
// let req = TestRequest::with_uri("/app/t1/path1")
|
||||
// .method(Method::GET)
|
||||
// .request();
|
||||
// let resp = app.run(req);
|
||||
// assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
// }
|
||||
let req = TestRequest::with_uri("/app/t1/path1")
|
||||
.method(Method::GET)
|
||||
.to_request();
|
||||
let resp = rt.block_on(srv.call(req)).unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_scope_with_variable_segment() {
|
||||
|
|
|
@ -8,7 +8,7 @@ use actix_http::{
|
|||
Error, Extensions, HttpMessage, Payload, Request, RequestHead, Response,
|
||||
ResponseHead,
|
||||
};
|
||||
use actix_router::{Path, Url};
|
||||
use actix_router::{Path, Resource, Url};
|
||||
use futures::future::{ok, FutureResult, IntoFuture};
|
||||
|
||||
use crate::request::HttpRequest;
|
||||
|
@ -137,6 +137,12 @@ impl<P> ServiceRequest<P> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<P> Resource<Url> for ServiceRequest<P> {
|
||||
fn resource_path(&mut self) -> &mut Path<Url> {
|
||||
self.match_info_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> HttpMessage for ServiceRequest<P> {
|
||||
type Stream = P;
|
||||
|
||||
|
|
Loading…
Reference in a new issue