1use std::{cell::RefCell, fmt, future::Future, mem, rc::Rc};
2
3use actix_http::{body::MessageBody, Extensions};
4use actix_router::{ResourceDef, Router};
5use actix_service::{
6 apply, apply_fn_factory, boxed, IntoServiceFactory, Service, ServiceFactory, ServiceFactoryExt,
7 Transform,
8};
9use futures_core::future::LocalBoxFuture;
10use futures_util::future::join_all;
11
12use crate::{
13 config::ServiceConfig,
14 data::Data,
15 dev::AppService,
16 guard::Guard,
17 rmap::ResourceMap,
18 service::{
19 AppServiceFactory, BoxedHttpService, BoxedHttpServiceFactory, HttpServiceFactory,
20 ServiceFactoryWrapper, ServiceRequest, ServiceResponse,
21 },
22 Error, Resource, Route,
23};
24
25type Guards = Vec<Box<dyn Guard>>;
26
27pub struct Scope<T = ScopeEndpoint> {
58 endpoint: T,
59 rdef: String,
60 app_data: Option<Extensions>,
61 services: Vec<Box<dyn AppServiceFactory>>,
62 guards: Vec<Box<dyn Guard>>,
63 default: Option<Rc<BoxedHttpServiceFactory>>,
64 external: Vec<ResourceDef>,
65 factory_ref: Rc<RefCell<Option<ScopeFactory>>>,
66}
67
68impl Scope {
69 pub fn new(path: &str) -> Scope {
71 let factory_ref = Rc::new(RefCell::new(None));
72
73 Scope {
74 endpoint: ScopeEndpoint::new(Rc::clone(&factory_ref)),
75 rdef: path.to_string(),
76 app_data: None,
77 guards: Vec::new(),
78 services: Vec::new(),
79 default: None,
80 external: Vec::new(),
81 factory_ref,
82 }
83 }
84}
85
86impl<T> Scope<T>
87where
88 T: ServiceFactory<ServiceRequest, Config = (), Error = Error, InitError = ()>,
89{
90 pub fn guard<G: Guard + 'static>(mut self, guard: G) -> Self {
109 self.guards.push(Box::new(guard));
110 self
111 }
112
113 #[doc(alias = "manage")]
146 pub fn app_data<U: 'static>(mut self, data: U) -> Self {
147 self.app_data
148 .get_or_insert_with(Extensions::new)
149 .insert(data);
150
151 self
152 }
153
154 #[deprecated(since = "4.0.0", note = "Use `.app_data(Data::new(val))` instead.")]
158 pub fn data<U: 'static>(self, data: U) -> Self {
159 self.app_data(Data::new(data))
160 }
161
162 pub fn configure<F>(mut self, cfg_fn: F) -> Self
187 where
188 F: FnOnce(&mut ServiceConfig),
189 {
190 let mut cfg = ServiceConfig::new();
191 cfg_fn(&mut cfg);
192
193 self.services.extend(cfg.services);
194 self.external.extend(cfg.external);
195
196 self.app_data
198 .get_or_insert_with(Extensions::new)
199 .extend(cfg.app_data);
200
201 if let Some(default) = cfg.default {
202 self.default = Some(default);
203 }
204
205 self
206 }
207
208 pub fn service<F>(mut self, factory: F) -> Self
233 where
234 F: HttpServiceFactory + 'static,
235 {
236 self.services
237 .push(Box::new(ServiceFactoryWrapper::new(factory)));
238 self
239 }
240
241 pub fn route(self, path: &str, mut route: Route) -> Self {
261 self.service(
262 Resource::new(path)
263 .add_guards(route.take_guards())
264 .route(route),
265 )
266 }
267
268 pub fn default_service<F, U>(mut self, f: F) -> Self
273 where
274 F: IntoServiceFactory<U, ServiceRequest>,
275 U: ServiceFactory<ServiceRequest, Config = (), Response = ServiceResponse, Error = Error>
276 + 'static,
277 U::InitError: fmt::Debug,
278 {
279 self.default = Some(Rc::new(boxed::factory(f.into_factory().map_init_err(
281 |err| {
282 log::error!("Can not construct default service: {err:?}");
283 },
284 ))));
285
286 self
287 }
288
289 #[doc(alias = "middleware")]
296 #[doc(alias = "use")] pub fn wrap<M, B>(
298 self,
299 mw: M,
300 ) -> Scope<
301 impl ServiceFactory<
302 ServiceRequest,
303 Config = (),
304 Response = ServiceResponse<B>,
305 Error = Error,
306 InitError = (),
307 >,
308 >
309 where
310 M: Transform<
311 T::Service,
312 ServiceRequest,
313 Response = ServiceResponse<B>,
314 Error = Error,
315 InitError = (),
316 > + 'static,
317 B: MessageBody,
318 {
319 Scope {
320 endpoint: apply(mw, self.endpoint),
321 rdef: self.rdef,
322 app_data: self.app_data,
323 guards: self.guards,
324 services: self.services,
325 default: self.default,
326 external: self.external,
327 factory_ref: self.factory_ref,
328 }
329 }
330
331 #[doc(alias = "middleware")]
339 #[doc(alias = "use")] pub fn wrap_fn<F, R, B>(
341 self,
342 mw: F,
343 ) -> Scope<
344 impl ServiceFactory<
345 ServiceRequest,
346 Config = (),
347 Response = ServiceResponse<B>,
348 Error = Error,
349 InitError = (),
350 >,
351 >
352 where
353 F: Fn(ServiceRequest, &T::Service) -> R + Clone + 'static,
354 R: Future<Output = Result<ServiceResponse<B>, Error>>,
355 B: MessageBody,
356 {
357 Scope {
358 endpoint: apply_fn_factory(self.endpoint, mw),
359 rdef: self.rdef,
360 app_data: self.app_data,
361 guards: self.guards,
362 services: self.services,
363 default: self.default,
364 external: self.external,
365 factory_ref: self.factory_ref,
366 }
367 }
368}
369
370impl<T, B> HttpServiceFactory for Scope<T>
371where
372 T: ServiceFactory<
373 ServiceRequest,
374 Config = (),
375 Response = ServiceResponse<B>,
376 Error = Error,
377 InitError = (),
378 > + 'static,
379 B: MessageBody + 'static,
380{
381 fn register(mut self, config: &mut AppService) {
382 let default = self.default.unwrap_or_else(|| config.default_service());
384
385 let mut cfg = config.clone_config();
387 self.services
388 .into_iter()
389 .for_each(|mut srv| srv.register(&mut cfg));
390
391 let mut rmap = ResourceMap::new(ResourceDef::root_prefix(&self.rdef));
392
393 for mut rdef in mem::take(&mut self.external) {
395 rmap.add(&mut rdef, None);
396 }
397
398 *self.factory_ref.borrow_mut() = Some(ScopeFactory {
400 default,
401 services: cfg
402 .into_services()
403 .1
404 .into_iter()
405 .map(|(mut rdef, srv, guards, nested)| {
406 rmap.add(&mut rdef, nested);
407 (rdef, srv, RefCell::new(guards))
408 })
409 .collect::<Vec<_>>()
410 .into_boxed_slice()
411 .into(),
412 });
413
414 let guards = if self.guards.is_empty() {
416 None
417 } else {
418 Some(self.guards)
419 };
420
421 let scope_data = self.app_data.map(Rc::new);
422
423 let endpoint = apply_fn_factory(self.endpoint, move |mut req: ServiceRequest, srv| {
425 if let Some(ref data) = scope_data {
426 req.add_data_container(Rc::clone(data));
427 }
428
429 let fut = srv.call(req);
430
431 async { Ok(fut.await?.map_into_boxed_body()) }
432 });
433
434 config.register_service(
436 ResourceDef::root_prefix(&self.rdef),
437 guards,
438 endpoint,
439 Some(Rc::new(rmap)),
440 )
441 }
442}
443
444pub struct ScopeFactory {
445 #[allow(clippy::type_complexity)]
446 services: Rc<
447 [(
448 ResourceDef,
449 BoxedHttpServiceFactory,
450 RefCell<Option<Guards>>,
451 )],
452 >,
453 default: Rc<BoxedHttpServiceFactory>,
454}
455
456impl ServiceFactory<ServiceRequest> for ScopeFactory {
457 type Response = ServiceResponse;
458 type Error = Error;
459 type Config = ();
460 type Service = ScopeService;
461 type InitError = ();
462 type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
463
464 fn new_service(&self, _: ()) -> Self::Future {
465 let default_fut = self.default.new_service(());
467
468 let factory_fut = join_all(self.services.iter().map(|(path, factory, guards)| {
470 let path = path.clone();
471 let guards = guards.borrow_mut().take().unwrap_or_default();
472 let factory_fut = factory.new_service(());
473 async move {
474 factory_fut
475 .await
476 .map(move |service| (path, guards, service))
477 }
478 }));
479
480 Box::pin(async move {
481 let default = default_fut.await?;
482
483 let router = factory_fut
485 .await
486 .into_iter()
487 .collect::<Result<Vec<_>, _>>()?
488 .drain(..)
489 .fold(Router::build(), |mut router, (path, guards, service)| {
490 router.push(path, service, guards);
491 router
492 })
493 .finish();
494
495 Ok(ScopeService { router, default })
496 })
497 }
498}
499
500pub struct ScopeService {
501 router: Router<BoxedHttpService, Vec<Box<dyn Guard>>>,
502 default: BoxedHttpService,
503}
504
505impl Service<ServiceRequest> for ScopeService {
506 type Response = ServiceResponse;
507 type Error = Error;
508 type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
509
510 actix_service::always_ready!();
511
512 fn call(&self, mut req: ServiceRequest) -> Self::Future {
513 let res = self.router.recognize_fn(&mut req, |req, guards| {
514 let guard_ctx = req.guard_ctx();
515 guards.iter().all(|guard| guard.check(&guard_ctx))
516 });
517
518 if let Some((srv, _info)) = res {
519 srv.call(req)
520 } else {
521 self.default.call(req)
522 }
523 }
524}
525
526#[doc(hidden)]
527pub struct ScopeEndpoint {
528 factory: Rc<RefCell<Option<ScopeFactory>>>,
529}
530
531impl ScopeEndpoint {
532 fn new(factory: Rc<RefCell<Option<ScopeFactory>>>) -> Self {
533 ScopeEndpoint { factory }
534 }
535}
536
537impl ServiceFactory<ServiceRequest> for ScopeEndpoint {
538 type Response = ServiceResponse;
539 type Error = Error;
540 type Config = ();
541 type Service = ScopeService;
542 type InitError = ();
543 type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
544
545 fn new_service(&self, _: ()) -> Self::Future {
546 self.factory.borrow_mut().as_mut().unwrap().new_service(())
547 }
548}
549
550#[cfg(test)]
551mod tests {
552 use actix_utils::future::ok;
553 use bytes::Bytes;
554
555 use super::*;
556 use crate::{
557 guard,
558 http::{
559 header::{self, HeaderValue},
560 Method, StatusCode,
561 },
562 middleware::DefaultHeaders,
563 test::{assert_body_eq, call_service, init_service, read_body, TestRequest},
564 web, App, HttpMessage, HttpRequest, HttpResponse,
565 };
566
567 #[test]
568 fn can_be_returned_from_fn() {
569 fn my_scope_1() -> Scope {
570 web::scope("/test")
571 .service(web::resource("").route(web::get().to(|| async { "hello" })))
572 }
573
574 fn my_scope_2() -> Scope<
575 impl ServiceFactory<
576 ServiceRequest,
577 Config = (),
578 Response = ServiceResponse<impl MessageBody>,
579 Error = Error,
580 InitError = (),
581 >,
582 > {
583 web::scope("/test-compat")
584 .wrap_fn(|req, srv| {
585 let fut = srv.call(req);
586 async { Ok(fut.await?.map_into_right_body::<()>()) }
587 })
588 .service(web::resource("").route(web::get().to(|| async { "hello" })))
589 }
590
591 fn my_scope_3() -> impl HttpServiceFactory {
592 my_scope_2()
593 }
594
595 App::new()
596 .service(my_scope_1())
597 .service(my_scope_2())
598 .service(my_scope_3());
599 }
600
601 #[actix_rt::test]
602 async fn test_scope() {
603 let srv = init_service(
604 App::new()
605 .service(web::scope("/app").service(web::resource("/path1").to(HttpResponse::Ok))),
606 )
607 .await;
608
609 let req = TestRequest::with_uri("/app/path1").to_request();
610 let resp = srv.call(req).await.unwrap();
611 assert_eq!(resp.status(), StatusCode::OK);
612 }
613
614 #[actix_rt::test]
615 async fn test_scope_root() {
616 let srv = init_service(
617 App::new().service(
618 web::scope("/app")
619 .service(web::resource("").to(HttpResponse::Ok))
620 .service(web::resource("/").to(HttpResponse::Created)),
621 ),
622 )
623 .await;
624
625 let req = TestRequest::with_uri("/app").to_request();
626 let resp = srv.call(req).await.unwrap();
627 assert_eq!(resp.status(), StatusCode::OK);
628
629 let req = TestRequest::with_uri("/app/").to_request();
630 let resp = srv.call(req).await.unwrap();
631 assert_eq!(resp.status(), StatusCode::CREATED);
632 }
633
634 #[actix_rt::test]
635 async fn test_scope_root2() {
636 let srv = init_service(
637 App::new().service(web::scope("/app/").service(web::resource("").to(HttpResponse::Ok))),
638 )
639 .await;
640
641 let req = TestRequest::with_uri("/app").to_request();
642 let resp = srv.call(req).await.unwrap();
643 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
644
645 let req = TestRequest::with_uri("/app/").to_request();
646 let resp = srv.call(req).await.unwrap();
647 assert_eq!(resp.status(), StatusCode::OK);
648 }
649
650 #[actix_rt::test]
651 async fn test_scope_root3() {
652 let srv = init_service(
653 App::new()
654 .service(web::scope("/app/").service(web::resource("/").to(HttpResponse::Ok))),
655 )
656 .await;
657
658 let req = TestRequest::with_uri("/app").to_request();
659 let resp = srv.call(req).await.unwrap();
660 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
661
662 let req = TestRequest::with_uri("/app/").to_request();
663 let resp = srv.call(req).await.unwrap();
664 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
665 }
666
667 #[actix_rt::test]
668 async fn test_scope_route() {
669 let srv = init_service(
670 App::new().service(
671 web::scope("app")
672 .route("/path1", web::get().to(HttpResponse::Ok))
673 .route("/path1", web::delete().to(HttpResponse::Ok)),
674 ),
675 )
676 .await;
677
678 let req = TestRequest::with_uri("/app/path1").to_request();
679 let resp = srv.call(req).await.unwrap();
680 assert_eq!(resp.status(), StatusCode::OK);
681
682 let req = TestRequest::with_uri("/app/path1")
683 .method(Method::DELETE)
684 .to_request();
685 let resp = srv.call(req).await.unwrap();
686 assert_eq!(resp.status(), StatusCode::OK);
687
688 let req = TestRequest::with_uri("/app/path1")
689 .method(Method::POST)
690 .to_request();
691 let resp = srv.call(req).await.unwrap();
692 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
693 }
694
695 #[actix_rt::test]
696 async fn test_scope_route_without_leading_slash() {
697 let srv = init_service(
698 App::new().service(
699 web::scope("app").service(
700 web::resource("path1")
701 .route(web::get().to(HttpResponse::Ok))
702 .route(web::delete().to(HttpResponse::Ok)),
703 ),
704 ),
705 )
706 .await;
707
708 let req = TestRequest::with_uri("/app/path1").to_request();
709 let resp = srv.call(req).await.unwrap();
710 assert_eq!(resp.status(), StatusCode::OK);
711
712 let req = TestRequest::with_uri("/app/path1")
713 .method(Method::DELETE)
714 .to_request();
715 let resp = srv.call(req).await.unwrap();
716 assert_eq!(resp.status(), StatusCode::OK);
717
718 let req = TestRequest::with_uri("/app/path1")
719 .method(Method::POST)
720 .to_request();
721 let resp = srv.call(req).await.unwrap();
722 assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
723 }
724
725 #[actix_rt::test]
726 async fn test_scope_guard() {
727 let srv = init_service(
728 App::new().service(
729 web::scope("/app")
730 .guard(guard::Get())
731 .service(web::resource("/path1").to(HttpResponse::Ok)),
732 ),
733 )
734 .await;
735
736 let req = TestRequest::with_uri("/app/path1")
737 .method(Method::POST)
738 .to_request();
739 let resp = srv.call(req).await.unwrap();
740 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
741
742 let req = TestRequest::with_uri("/app/path1")
743 .method(Method::GET)
744 .to_request();
745 let resp = srv.call(req).await.unwrap();
746 assert_eq!(resp.status(), StatusCode::OK);
747 }
748
749 #[actix_rt::test]
750 async fn test_scope_variable_segment() {
751 let srv = init_service(App::new().service(web::scope("/ab-{project}").service(
752 web::resource("/path1").to(|r: HttpRequest| {
753 HttpResponse::Ok().body(format!("project: {}", &r.match_info()["project"]))
754 }),
755 )))
756 .await;
757
758 let req = TestRequest::with_uri("/ab-project1/path1").to_request();
759 let res = srv.call(req).await.unwrap();
760 assert_eq!(res.status(), StatusCode::OK);
761 assert_body_eq!(res, b"project: project1");
762
763 let req = TestRequest::with_uri("/aa-project1/path1").to_request();
764 let res = srv.call(req).await.unwrap();
765 assert_eq!(res.status(), StatusCode::NOT_FOUND);
766 }
767
768 #[actix_rt::test]
769 async fn test_nested_scope() {
770 let srv = init_service(App::new().service(web::scope("/app").service(
771 web::scope("/t1").service(web::resource("/path1").to(HttpResponse::Created)),
772 )))
773 .await;
774
775 let req = TestRequest::with_uri("/app/t1/path1").to_request();
776 let resp = srv.call(req).await.unwrap();
777 assert_eq!(resp.status(), StatusCode::CREATED);
778 }
779
780 #[actix_rt::test]
781 async fn test_nested_scope_no_slash() {
782 let srv =
783 init_service(App::new().service(web::scope("/app").service(
784 web::scope("t1").service(web::resource("/path1").to(HttpResponse::Created)),
785 )))
786 .await;
787
788 let req = TestRequest::with_uri("/app/t1/path1").to_request();
789 let resp = srv.call(req).await.unwrap();
790 assert_eq!(resp.status(), StatusCode::CREATED);
791 }
792
793 #[actix_rt::test]
794 async fn test_nested_scope_root() {
795 let srv = init_service(
796 App::new().service(
797 web::scope("/app").service(
798 web::scope("/t1")
799 .service(web::resource("").to(HttpResponse::Ok))
800 .service(web::resource("/").to(HttpResponse::Created)),
801 ),
802 ),
803 )
804 .await;
805
806 let req = TestRequest::with_uri("/app/t1").to_request();
807 let resp = srv.call(req).await.unwrap();
808 assert_eq!(resp.status(), StatusCode::OK);
809
810 let req = TestRequest::with_uri("/app/t1/").to_request();
811 let resp = srv.call(req).await.unwrap();
812 assert_eq!(resp.status(), StatusCode::CREATED);
813 }
814
815 #[actix_rt::test]
816 async fn test_nested_scope_filter() {
817 let srv = init_service(
818 App::new().service(
819 web::scope("/app").service(
820 web::scope("/t1")
821 .guard(guard::Get())
822 .service(web::resource("/path1").to(HttpResponse::Ok)),
823 ),
824 ),
825 )
826 .await;
827
828 let req = TestRequest::with_uri("/app/t1/path1")
829 .method(Method::POST)
830 .to_request();
831 let resp = srv.call(req).await.unwrap();
832 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
833
834 let req = TestRequest::with_uri("/app/t1/path1")
835 .method(Method::GET)
836 .to_request();
837 let resp = srv.call(req).await.unwrap();
838 assert_eq!(resp.status(), StatusCode::OK);
839 }
840
841 #[actix_rt::test]
842 async fn test_nested_scope_with_variable_segment() {
843 let srv = init_service(App::new().service(web::scope("/app").service(
844 web::scope("/{project_id}").service(web::resource("/path1").to(|r: HttpRequest| {
845 HttpResponse::Created().body(format!("project: {}", &r.match_info()["project_id"]))
846 })),
847 )))
848 .await;
849
850 let req = TestRequest::with_uri("/app/project_1/path1").to_request();
851 let res = srv.call(req).await.unwrap();
852 assert_eq!(res.status(), StatusCode::CREATED);
853 assert_body_eq!(res, b"project: project_1");
854 }
855
856 #[actix_rt::test]
857 async fn test_nested2_scope_with_variable_segment() {
858 let srv = init_service(App::new().service(web::scope("/app").service(
859 web::scope("/{project}").service(web::scope("/{id}").service(
860 web::resource("/path1").to(|r: HttpRequest| {
861 HttpResponse::Created().body(format!(
862 "project: {} - {}",
863 &r.match_info()["project"],
864 &r.match_info()["id"],
865 ))
866 }),
867 )),
868 )))
869 .await;
870
871 let req = TestRequest::with_uri("/app/test/1/path1").to_request();
872 let res = srv.call(req).await.unwrap();
873 assert_eq!(res.status(), StatusCode::CREATED);
874 assert_body_eq!(res, b"project: test - 1");
875
876 let req = TestRequest::with_uri("/app/test/1/path2").to_request();
877 let res = srv.call(req).await.unwrap();
878 assert_eq!(res.status(), StatusCode::NOT_FOUND);
879 }
880
881 #[actix_rt::test]
882 async fn test_default_resource() {
883 let srv = init_service(
884 App::new().service(
885 web::scope("/app")
886 .service(web::resource("/path1").to(HttpResponse::Ok))
887 .default_service(|r: ServiceRequest| {
888 ok(r.into_response(HttpResponse::BadRequest()))
889 }),
890 ),
891 )
892 .await;
893
894 let req = TestRequest::with_uri("/app/path2").to_request();
895 let resp = srv.call(req).await.unwrap();
896 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
897
898 let req = TestRequest::with_uri("/path2").to_request();
899 let resp = srv.call(req).await.unwrap();
900 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
901 }
902
903 #[actix_rt::test]
904 async fn test_default_resource_propagation() {
905 let srv = init_service(
906 App::new()
907 .service(web::scope("/app1").default_service(web::to(HttpResponse::BadRequest)))
908 .service(web::scope("/app2"))
909 .default_service(|r: ServiceRequest| {
910 ok(r.into_response(HttpResponse::MethodNotAllowed()))
911 }),
912 )
913 .await;
914
915 let req = TestRequest::with_uri("/non-exist").to_request();
916 let resp = srv.call(req).await.unwrap();
917 assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
918
919 let req = TestRequest::with_uri("/app1/non-exist").to_request();
920 let resp = srv.call(req).await.unwrap();
921 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
922
923 let req = TestRequest::with_uri("/app2/non-exist").to_request();
924 let resp = srv.call(req).await.unwrap();
925 assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
926 }
927
928 #[actix_rt::test]
929 async fn test_middleware() {
930 let srv = init_service(
931 App::new().service(
932 web::scope("app")
933 .wrap(
934 DefaultHeaders::new()
935 .add((header::CONTENT_TYPE, HeaderValue::from_static("0001"))),
936 )
937 .service(web::resource("/test").route(web::get().to(HttpResponse::Ok))),
938 ),
939 )
940 .await;
941
942 let req = TestRequest::with_uri("/app/test").to_request();
943 let resp = call_service(&srv, req).await;
944 assert_eq!(resp.status(), StatusCode::OK);
945 assert_eq!(
946 resp.headers().get(header::CONTENT_TYPE).unwrap(),
947 HeaderValue::from_static("0001")
948 );
949 }
950
951 #[actix_rt::test]
952 async fn test_middleware_body_type() {
953 let srv = init_service(
955 App::new().service(
956 web::scope("app")
957 .wrap_fn(|req, srv| {
958 let fut = srv.call(req);
959 async { Ok(fut.await?.map_into_right_body::<()>()) }
960 })
961 .service(web::resource("/test").route(web::get().to(|| async { "hello" }))),
962 ),
963 )
964 .await;
965
966 use actix_http::body::MessageBody as _;
968 let req = TestRequest::with_uri("/app/test").to_request();
969 let resp = call_service(&srv, req).await;
970 let body = resp.into_body();
971 assert_eq!(body.try_into_bytes().unwrap(), b"hello".as_ref());
972 }
973
974 #[actix_rt::test]
975 async fn test_middleware_fn() {
976 let srv = init_service(
977 App::new().service(
978 web::scope("app")
979 .wrap_fn(|req, srv| {
980 let fut = srv.call(req);
981 async move {
982 let mut res = fut.await?;
983 res.headers_mut()
984 .insert(header::CONTENT_TYPE, HeaderValue::from_static("0001"));
985 Ok(res)
986 }
987 })
988 .route("/test", web::get().to(HttpResponse::Ok)),
989 ),
990 )
991 .await;
992
993 let req = TestRequest::with_uri("/app/test").to_request();
994 let resp = call_service(&srv, req).await;
995 assert_eq!(resp.status(), StatusCode::OK);
996 assert_eq!(
997 resp.headers().get(header::CONTENT_TYPE).unwrap(),
998 HeaderValue::from_static("0001")
999 );
1000 }
1001
1002 #[actix_rt::test]
1003 async fn test_middleware_app_data() {
1004 let srv = init_service(
1005 App::new().service(
1006 web::scope("app")
1007 .app_data(1usize)
1008 .wrap_fn(|req, srv| {
1009 assert_eq!(req.app_data::<usize>(), Some(&1usize));
1010 req.extensions_mut().insert(1usize);
1011 srv.call(req)
1012 })
1013 .route("/test", web::get().to(HttpResponse::Ok))
1014 .default_service(|req: ServiceRequest| async move {
1015 let (req, _) = req.into_parts();
1016
1017 assert_eq!(req.extensions().get::<usize>(), Some(&1));
1018
1019 Ok(ServiceResponse::new(
1020 req,
1021 HttpResponse::BadRequest().finish(),
1022 ))
1023 }),
1024 ),
1025 )
1026 .await;
1027
1028 let req = TestRequest::with_uri("/app/test").to_request();
1029 let resp = call_service(&srv, req).await;
1030 assert_eq!(resp.status(), StatusCode::OK);
1031
1032 let req = TestRequest::with_uri("/app/default").to_request();
1033 let resp = call_service(&srv, req).await;
1034 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
1035 }
1036
1037 #[allow(deprecated)]
1039 #[actix_rt::test]
1040 async fn test_override_data() {
1041 let srv = init_service(App::new().data(1usize).service(
1042 web::scope("app").data(10usize).route(
1043 "/t",
1044 web::get().to(|data: web::Data<usize>| {
1045 assert_eq!(**data, 10);
1046 HttpResponse::Ok()
1047 }),
1048 ),
1049 ))
1050 .await;
1051
1052 let req = TestRequest::with_uri("/app/t").to_request();
1053 let resp = call_service(&srv, req).await;
1054 assert_eq!(resp.status(), StatusCode::OK);
1055 }
1056
1057 #[allow(deprecated)]
1059 #[actix_rt::test]
1060 async fn test_override_data_default_service() {
1061 let srv =
1062 init_service(App::new().data(1usize).service(
1063 web::scope("app").data(10usize).default_service(web::to(
1064 |data: web::Data<usize>| {
1065 assert_eq!(**data, 10);
1066 HttpResponse::Ok()
1067 },
1068 )),
1069 ))
1070 .await;
1071
1072 let req = TestRequest::with_uri("/app/t").to_request();
1073 let resp = call_service(&srv, req).await;
1074 assert_eq!(resp.status(), StatusCode::OK);
1075 }
1076
1077 #[actix_rt::test]
1078 async fn test_override_app_data() {
1079 let srv = init_service(App::new().app_data(web::Data::new(1usize)).service(
1080 web::scope("app").app_data(web::Data::new(10usize)).route(
1081 "/t",
1082 web::get().to(|data: web::Data<usize>| {
1083 assert_eq!(**data, 10);
1084 HttpResponse::Ok()
1085 }),
1086 ),
1087 ))
1088 .await;
1089
1090 let req = TestRequest::with_uri("/app/t").to_request();
1091 let resp = call_service(&srv, req).await;
1092 assert_eq!(resp.status(), StatusCode::OK);
1093 }
1094
1095 #[actix_rt::test]
1096 async fn test_scope_config() {
1097 let srv = init_service(App::new().service(web::scope("/app").configure(|s| {
1098 s.route("/path1", web::get().to(HttpResponse::Ok));
1099 })))
1100 .await;
1101
1102 let req = TestRequest::with_uri("/app/path1").to_request();
1103 let resp = srv.call(req).await.unwrap();
1104 assert_eq!(resp.status(), StatusCode::OK);
1105 }
1106
1107 #[actix_rt::test]
1108 async fn test_scope_config_2() {
1109 let srv = init_service(App::new().service(web::scope("/app").configure(|s| {
1110 s.service(web::scope("/v1").configure(|s| {
1111 s.route("/", web::get().to(HttpResponse::Ok));
1112 }));
1113 })))
1114 .await;
1115
1116 let req = TestRequest::with_uri("/app/v1/").to_request();
1117 let resp = srv.call(req).await.unwrap();
1118 assert_eq!(resp.status(), StatusCode::OK);
1119 }
1120
1121 #[actix_rt::test]
1122 async fn test_url_for_external() {
1123 let srv = init_service(App::new().service(web::scope("/app").configure(|s| {
1124 s.service(web::scope("/v1").configure(|s| {
1125 s.external_resource("youtube", "https://youtube.com/watch/{video_id}");
1126 s.route(
1127 "/",
1128 web::get().to(|req: HttpRequest| {
1129 HttpResponse::Ok()
1130 .body(req.url_for("youtube", ["xxxxxx"]).unwrap().to_string())
1131 }),
1132 );
1133 }));
1134 })))
1135 .await;
1136
1137 let req = TestRequest::with_uri("/app/v1/").to_request();
1138 let resp = srv.call(req).await.unwrap();
1139 assert_eq!(resp.status(), StatusCode::OK);
1140 let body = read_body(resp).await;
1141 assert_eq!(body, &b"https://youtube.com/watch/xxxxxx"[..]);
1142 }
1143
1144 #[actix_rt::test]
1145 async fn test_url_for_nested() {
1146 let srv = init_service(App::new().service(web::scope("/a").service(
1147 web::scope("/b").service(web::resource("/c/{stuff}").name("c").route(web::get().to(
1148 |req: HttpRequest| {
1149 HttpResponse::Ok().body(format!("{}", req.url_for("c", ["12345"]).unwrap()))
1150 },
1151 ))),
1152 )))
1153 .await;
1154
1155 let req = TestRequest::with_uri("/a/b/c/test").to_request();
1156 let resp = call_service(&srv, req).await;
1157 assert_eq!(resp.status(), StatusCode::OK);
1158 let body = read_body(resp).await;
1159 assert_eq!(
1160 body,
1161 Bytes::from_static(b"http://localhost:8080/a/b/c/12345")
1162 );
1163 }
1164
1165 #[actix_rt::test]
1166 async fn dynamic_scopes() {
1167 let srv = init_service(
1168 App::new().service(
1169 web::scope("/{a}/").service(
1170 web::scope("/{b}/")
1171 .route("", web::get().to(|_: HttpRequest| HttpResponse::Created()))
1172 .route(
1173 "/",
1174 web::get().to(|_: HttpRequest| HttpResponse::Accepted()),
1175 )
1176 .route("/{c}", web::get().to(|_: HttpRequest| HttpResponse::Ok())),
1177 ),
1178 ),
1179 )
1180 .await;
1181
1182 let req = TestRequest::with_uri("/a//b//c").to_request();
1184 let resp = call_service(&srv, req).await;
1185 assert_eq!(resp.status(), StatusCode::OK);
1186
1187 let req = TestRequest::with_uri("/a//b/").to_request();
1188 let resp = call_service(&srv, req).await;
1189 assert_eq!(resp.status(), StatusCode::CREATED);
1190
1191 let req = TestRequest::with_uri("/a//b//").to_request();
1192 let resp = call_service(&srv, req).await;
1193 assert_eq!(resp.status(), StatusCode::ACCEPTED);
1194
1195 let req = TestRequest::with_uri("/a//b//c/d").to_request();
1196 let resp = call_service(&srv, req).await;
1197 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
1198
1199 let srv = init_service(
1200 App::new().service(
1201 web::scope("/{a}").service(
1202 web::scope("/{b}")
1203 .route("", web::get().to(|_: HttpRequest| HttpResponse::Created()))
1204 .route(
1205 "/",
1206 web::get().to(|_: HttpRequest| HttpResponse::Accepted()),
1207 )
1208 .route("/{c}", web::get().to(|_: HttpRequest| HttpResponse::Ok())),
1209 ),
1210 ),
1211 )
1212 .await;
1213
1214 let req = TestRequest::with_uri("/a/b/c").to_request();
1215 let resp = call_service(&srv, req).await;
1216 assert_eq!(resp.status(), StatusCode::OK);
1217
1218 let req = TestRequest::with_uri("/a/b").to_request();
1219 let resp = call_service(&srv, req).await;
1220 assert_eq!(resp.status(), StatusCode::CREATED);
1221
1222 let req = TestRequest::with_uri("/a/b/").to_request();
1223 let resp = call_service(&srv, req).await;
1224 assert_eq!(resp.status(), StatusCode::ACCEPTED);
1225
1226 let req = TestRequest::with_uri("/a/b/c/d").to_request();
1227 let resp = call_service(&srv, req).await;
1228 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
1229 }
1230}