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
388 #[cfg(feature = "experimental-introspection")]
390 {
391 let scope_id = config.prepare_scope_id();
392 cfg.scope_id_stack.push(scope_id);
393 cfg.update_prefix(&self.rdef);
394 }
395
396 self.services
397 .into_iter()
398 .for_each(|mut srv| srv.register(&mut cfg));
399
400 let mut rmap = ResourceMap::new(ResourceDef::root_prefix(&self.rdef));
401
402 #[cfg(feature = "experimental-introspection")]
403 let origin_scope = cfg.current_prefix.clone();
404
405 for mut rdef in mem::take(&mut self.external) {
407 #[cfg(feature = "experimental-introspection")]
408 {
409 cfg.introspector
410 .borrow_mut()
411 .register_external(&rdef, &origin_scope);
412 }
413 rmap.add(&mut rdef, None);
414 }
415
416 *self.factory_ref.borrow_mut() = Some(ScopeFactory {
418 default,
419 services: cfg
420 .into_services()
421 .1
422 .into_iter()
423 .map(|(mut rdef, srv, guards, nested)| {
424 rmap.add(&mut rdef, nested);
425 (rdef, srv, RefCell::new(guards))
426 })
427 .collect::<Vec<_>>()
428 .into_boxed_slice()
429 .into(),
430 });
431
432 let guards = if self.guards.is_empty() {
434 None
435 } else {
436 Some(self.guards)
437 };
438
439 let scope_data = self.app_data.map(Rc::new);
440
441 let endpoint = apply_fn_factory(self.endpoint, move |mut req: ServiceRequest, srv| {
443 if let Some(ref data) = scope_data {
444 req.add_data_container(Rc::clone(data));
445 }
446
447 let fut = srv.call(req);
448
449 async { Ok(fut.await?.map_into_boxed_body()) }
450 });
451
452 config.register_service(
454 ResourceDef::root_prefix(&self.rdef),
455 guards,
456 endpoint,
457 Some(Rc::new(rmap)),
458 )
459 }
460}
461
462pub struct ScopeFactory {
463 #[allow(clippy::type_complexity)]
464 services: Rc<
465 [(
466 ResourceDef,
467 BoxedHttpServiceFactory,
468 RefCell<Option<Guards>>,
469 )],
470 >,
471 default: Rc<BoxedHttpServiceFactory>,
472}
473
474impl ServiceFactory<ServiceRequest> for ScopeFactory {
475 type Response = ServiceResponse;
476 type Error = Error;
477 type Config = ();
478 type Service = ScopeService;
479 type InitError = ();
480 type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
481
482 fn new_service(&self, _: ()) -> Self::Future {
483 let default_fut = self.default.new_service(());
485
486 let factory_fut = join_all(self.services.iter().map(|(path, factory, guards)| {
488 let path = path.clone();
489 let guards = guards.borrow_mut().take().unwrap_or_default();
490 let factory_fut = factory.new_service(());
491 async move {
492 factory_fut
493 .await
494 .map(move |service| (path, guards, service))
495 }
496 }));
497
498 Box::pin(async move {
499 let default = default_fut.await?;
500
501 let router = factory_fut
503 .await
504 .into_iter()
505 .collect::<Result<Vec<_>, _>>()?
506 .drain(..)
507 .fold(Router::build(), |mut router, (path, guards, service)| {
508 router.push(path, service, guards);
509 router
510 })
511 .finish();
512
513 Ok(ScopeService { router, default })
514 })
515 }
516}
517
518pub struct ScopeService {
519 router: Router<BoxedHttpService, Vec<Box<dyn Guard>>>,
520 default: BoxedHttpService,
521}
522
523impl Service<ServiceRequest> for ScopeService {
524 type Response = ServiceResponse;
525 type Error = Error;
526 type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
527
528 actix_service::always_ready!();
529
530 fn call(&self, mut req: ServiceRequest) -> Self::Future {
531 let res = self.router.recognize_fn(&mut req, |req, guards| {
532 let guard_ctx = req.guard_ctx();
533 guards.iter().all(|guard| guard.check(&guard_ctx))
534 });
535
536 if let Some((srv, _info)) = res {
537 srv.call(req)
538 } else {
539 self.default.call(req)
540 }
541 }
542}
543
544#[doc(hidden)]
545pub struct ScopeEndpoint {
546 factory: Rc<RefCell<Option<ScopeFactory>>>,
547}
548
549impl ScopeEndpoint {
550 fn new(factory: Rc<RefCell<Option<ScopeFactory>>>) -> Self {
551 ScopeEndpoint { factory }
552 }
553}
554
555impl ServiceFactory<ServiceRequest> for ScopeEndpoint {
556 type Response = ServiceResponse;
557 type Error = Error;
558 type Config = ();
559 type Service = ScopeService;
560 type InitError = ();
561 type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
562
563 fn new_service(&self, _: ()) -> Self::Future {
564 self.factory.borrow_mut().as_mut().unwrap().new_service(())
565 }
566}
567
568#[cfg(test)]
569mod tests {
570 use actix_utils::future::ok;
571 use bytes::Bytes;
572
573 use super::*;
574 use crate::{
575 guard,
576 http::{
577 header::{self, HeaderValue},
578 Method, StatusCode,
579 },
580 middleware::DefaultHeaders,
581 test::{assert_body_eq, call_service, init_service, read_body, TestRequest},
582 web, App, HttpMessage, HttpRequest, HttpResponse,
583 };
584
585 #[test]
586 fn can_be_returned_from_fn() {
587 fn my_scope_1() -> Scope {
588 web::scope("/test")
589 .service(web::resource("").route(web::get().to(|| async { "hello" })))
590 }
591
592 fn my_scope_2() -> Scope<
593 impl ServiceFactory<
594 ServiceRequest,
595 Config = (),
596 Response = ServiceResponse<impl MessageBody>,
597 Error = Error,
598 InitError = (),
599 >,
600 > {
601 web::scope("/test-compat")
602 .wrap_fn(|req, srv| {
603 let fut = srv.call(req);
604 async { Ok(fut.await?.map_into_right_body::<()>()) }
605 })
606 .service(web::resource("").route(web::get().to(|| async { "hello" })))
607 }
608
609 fn my_scope_3() -> impl HttpServiceFactory {
610 my_scope_2()
611 }
612
613 App::new()
614 .service(my_scope_1())
615 .service(my_scope_2())
616 .service(my_scope_3());
617 }
618
619 #[actix_rt::test]
620 async fn test_scope() {
621 let srv = init_service(
622 App::new()
623 .service(web::scope("/app").service(web::resource("/path1").to(HttpResponse::Ok))),
624 )
625 .await;
626
627 let req = TestRequest::with_uri("/app/path1").to_request();
628 let resp = srv.call(req).await.unwrap();
629 assert_eq!(resp.status(), StatusCode::OK);
630 }
631
632 #[actix_rt::test]
633 async fn test_scope_root() {
634 let srv = init_service(
635 App::new().service(
636 web::scope("/app")
637 .service(web::resource("").to(HttpResponse::Ok))
638 .service(web::resource("/").to(HttpResponse::Created)),
639 ),
640 )
641 .await;
642
643 let req = TestRequest::with_uri("/app").to_request();
644 let resp = srv.call(req).await.unwrap();
645 assert_eq!(resp.status(), StatusCode::OK);
646
647 let req = TestRequest::with_uri("/app/").to_request();
648 let resp = srv.call(req).await.unwrap();
649 assert_eq!(resp.status(), StatusCode::CREATED);
650 }
651
652 #[actix_rt::test]
653 async fn test_scope_root2() {
654 let srv = init_service(
655 App::new().service(web::scope("/app/").service(web::resource("").to(HttpResponse::Ok))),
656 )
657 .await;
658
659 let req = TestRequest::with_uri("/app").to_request();
660 let resp = srv.call(req).await.unwrap();
661 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
662
663 let req = TestRequest::with_uri("/app/").to_request();
664 let resp = srv.call(req).await.unwrap();
665 assert_eq!(resp.status(), StatusCode::OK);
666 }
667
668 #[actix_rt::test]
669 async fn test_scope_root3() {
670 let srv = init_service(
671 App::new()
672 .service(web::scope("/app/").service(web::resource("/").to(HttpResponse::Ok))),
673 )
674 .await;
675
676 let req = TestRequest::with_uri("/app").to_request();
677 let resp = srv.call(req).await.unwrap();
678 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
679
680 let req = TestRequest::with_uri("/app/").to_request();
681 let resp = srv.call(req).await.unwrap();
682 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
683 }
684
685 #[actix_rt::test]
686 async fn test_scope_route() {
687 let srv = init_service(
688 App::new().service(
689 web::scope("app")
690 .route("/path1", web::get().to(HttpResponse::Ok))
691 .route("/path1", web::delete().to(HttpResponse::Ok)),
692 ),
693 )
694 .await;
695
696 let req = TestRequest::with_uri("/app/path1").to_request();
697 let resp = srv.call(req).await.unwrap();
698 assert_eq!(resp.status(), StatusCode::OK);
699
700 let req = TestRequest::with_uri("/app/path1")
701 .method(Method::DELETE)
702 .to_request();
703 let resp = srv.call(req).await.unwrap();
704 assert_eq!(resp.status(), StatusCode::OK);
705
706 let req = TestRequest::with_uri("/app/path1")
707 .method(Method::POST)
708 .to_request();
709 let resp = srv.call(req).await.unwrap();
710 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
711 }
712
713 #[actix_rt::test]
714 async fn test_scope_route_without_leading_slash() {
715 let srv = init_service(
716 App::new().service(
717 web::scope("app").service(
718 web::resource("path1")
719 .route(web::get().to(HttpResponse::Ok))
720 .route(web::delete().to(HttpResponse::Ok)),
721 ),
722 ),
723 )
724 .await;
725
726 let req = TestRequest::with_uri("/app/path1").to_request();
727 let resp = srv.call(req).await.unwrap();
728 assert_eq!(resp.status(), StatusCode::OK);
729
730 let req = TestRequest::with_uri("/app/path1")
731 .method(Method::DELETE)
732 .to_request();
733 let resp = srv.call(req).await.unwrap();
734 assert_eq!(resp.status(), StatusCode::OK);
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::METHOD_NOT_ALLOWED);
741 }
742
743 #[actix_rt::test]
744 async fn test_scope_guard() {
745 let srv = init_service(
746 App::new().service(
747 web::scope("/app")
748 .guard(guard::Get())
749 .service(web::resource("/path1").to(HttpResponse::Ok)),
750 ),
751 )
752 .await;
753
754 let req = TestRequest::with_uri("/app/path1")
755 .method(Method::POST)
756 .to_request();
757 let resp = srv.call(req).await.unwrap();
758 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
759
760 let req = TestRequest::with_uri("/app/path1")
761 .method(Method::GET)
762 .to_request();
763 let resp = srv.call(req).await.unwrap();
764 assert_eq!(resp.status(), StatusCode::OK);
765 }
766
767 #[actix_rt::test]
768 async fn test_scope_variable_segment() {
769 let srv = init_service(App::new().service(web::scope("/ab-{project}").service(
770 web::resource("/path1").to(|r: HttpRequest| {
771 HttpResponse::Ok().body(format!("project: {}", &r.match_info()["project"]))
772 }),
773 )))
774 .await;
775
776 let req = TestRequest::with_uri("/ab-project1/path1").to_request();
777 let res = srv.call(req).await.unwrap();
778 assert_eq!(res.status(), StatusCode::OK);
779 assert_body_eq!(res, b"project: project1");
780
781 let req = TestRequest::with_uri("/aa-project1/path1").to_request();
782 let res = srv.call(req).await.unwrap();
783 assert_eq!(res.status(), StatusCode::NOT_FOUND);
784 }
785
786 #[actix_rt::test]
787 async fn test_nested_scope() {
788 let srv = init_service(App::new().service(web::scope("/app").service(
789 web::scope("/t1").service(web::resource("/path1").to(HttpResponse::Created)),
790 )))
791 .await;
792
793 let req = TestRequest::with_uri("/app/t1/path1").to_request();
794 let resp = srv.call(req).await.unwrap();
795 assert_eq!(resp.status(), StatusCode::CREATED);
796 }
797
798 #[actix_rt::test]
799 async fn test_nested_scope_no_slash() {
800 let srv =
801 init_service(App::new().service(web::scope("/app").service(
802 web::scope("t1").service(web::resource("/path1").to(HttpResponse::Created)),
803 )))
804 .await;
805
806 let req = TestRequest::with_uri("/app/t1/path1").to_request();
807 let resp = srv.call(req).await.unwrap();
808 assert_eq!(resp.status(), StatusCode::CREATED);
809 }
810
811 #[actix_rt::test]
812 async fn test_nested_scope_root() {
813 let srv = init_service(
814 App::new().service(
815 web::scope("/app").service(
816 web::scope("/t1")
817 .service(web::resource("").to(HttpResponse::Ok))
818 .service(web::resource("/").to(HttpResponse::Created)),
819 ),
820 ),
821 )
822 .await;
823
824 let req = TestRequest::with_uri("/app/t1").to_request();
825 let resp = srv.call(req).await.unwrap();
826 assert_eq!(resp.status(), StatusCode::OK);
827
828 let req = TestRequest::with_uri("/app/t1/").to_request();
829 let resp = srv.call(req).await.unwrap();
830 assert_eq!(resp.status(), StatusCode::CREATED);
831 }
832
833 #[actix_rt::test]
834 async fn test_nested_scope_filter() {
835 let srv = init_service(
836 App::new().service(
837 web::scope("/app").service(
838 web::scope("/t1")
839 .guard(guard::Get())
840 .service(web::resource("/path1").to(HttpResponse::Ok)),
841 ),
842 ),
843 )
844 .await;
845
846 let req = TestRequest::with_uri("/app/t1/path1")
847 .method(Method::POST)
848 .to_request();
849 let resp = srv.call(req).await.unwrap();
850 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
851
852 let req = TestRequest::with_uri("/app/t1/path1")
853 .method(Method::GET)
854 .to_request();
855 let resp = srv.call(req).await.unwrap();
856 assert_eq!(resp.status(), StatusCode::OK);
857 }
858
859 #[actix_rt::test]
860 async fn test_nested_scope_with_variable_segment() {
861 let srv = init_service(App::new().service(web::scope("/app").service(
862 web::scope("/{project_id}").service(web::resource("/path1").to(|r: HttpRequest| {
863 HttpResponse::Created().body(format!("project: {}", &r.match_info()["project_id"]))
864 })),
865 )))
866 .await;
867
868 let req = TestRequest::with_uri("/app/project_1/path1").to_request();
869 let res = srv.call(req).await.unwrap();
870 assert_eq!(res.status(), StatusCode::CREATED);
871 assert_body_eq!(res, b"project: project_1");
872 }
873
874 #[actix_rt::test]
875 async fn test_nested2_scope_with_variable_segment() {
876 let srv = init_service(App::new().service(web::scope("/app").service(
877 web::scope("/{project}").service(web::scope("/{id}").service(
878 web::resource("/path1").to(|r: HttpRequest| {
879 HttpResponse::Created().body(format!(
880 "project: {} - {}",
881 &r.match_info()["project"],
882 &r.match_info()["id"],
883 ))
884 }),
885 )),
886 )))
887 .await;
888
889 let req = TestRequest::with_uri("/app/test/1/path1").to_request();
890 let res = srv.call(req).await.unwrap();
891 assert_eq!(res.status(), StatusCode::CREATED);
892 assert_body_eq!(res, b"project: test - 1");
893
894 let req = TestRequest::with_uri("/app/test/1/path2").to_request();
895 let res = srv.call(req).await.unwrap();
896 assert_eq!(res.status(), StatusCode::NOT_FOUND);
897 }
898
899 #[actix_rt::test]
900 async fn test_default_resource() {
901 let srv = init_service(
902 App::new().service(
903 web::scope("/app")
904 .service(web::resource("/path1").to(HttpResponse::Ok))
905 .default_service(|r: ServiceRequest| {
906 ok(r.into_response(HttpResponse::BadRequest()))
907 }),
908 ),
909 )
910 .await;
911
912 let req = TestRequest::with_uri("/app/path2").to_request();
913 let resp = srv.call(req).await.unwrap();
914 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
915
916 let req = TestRequest::with_uri("/path2").to_request();
917 let resp = srv.call(req).await.unwrap();
918 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
919 }
920
921 #[actix_rt::test]
922 async fn test_default_resource_propagation() {
923 let srv = init_service(
924 App::new()
925 .service(web::scope("/app1").default_service(web::to(HttpResponse::BadRequest)))
926 .service(web::scope("/app2"))
927 .default_service(|r: ServiceRequest| {
928 ok(r.into_response(HttpResponse::MethodNotAllowed()))
929 }),
930 )
931 .await;
932
933 let req = TestRequest::with_uri("/non-exist").to_request();
934 let resp = srv.call(req).await.unwrap();
935 assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
936
937 let req = TestRequest::with_uri("/app1/non-exist").to_request();
938 let resp = srv.call(req).await.unwrap();
939 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
940
941 let req = TestRequest::with_uri("/app2/non-exist").to_request();
942 let resp = srv.call(req).await.unwrap();
943 assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
944 }
945
946 #[actix_rt::test]
947 async fn test_middleware() {
948 let srv = init_service(
949 App::new().service(
950 web::scope("app")
951 .wrap(
952 DefaultHeaders::new()
953 .add((header::CONTENT_TYPE, HeaderValue::from_static("0001"))),
954 )
955 .service(web::resource("/test").route(web::get().to(HttpResponse::Ok))),
956 ),
957 )
958 .await;
959
960 let req = TestRequest::with_uri("/app/test").to_request();
961 let resp = call_service(&srv, req).await;
962 assert_eq!(resp.status(), StatusCode::OK);
963 assert_eq!(
964 resp.headers().get(header::CONTENT_TYPE).unwrap(),
965 HeaderValue::from_static("0001")
966 );
967 }
968
969 #[actix_rt::test]
970 async fn test_middleware_body_type() {
971 let srv = init_service(
973 App::new().service(
974 web::scope("app")
975 .wrap_fn(|req, srv| {
976 let fut = srv.call(req);
977 async { Ok(fut.await?.map_into_right_body::<()>()) }
978 })
979 .service(web::resource("/test").route(web::get().to(|| async { "hello" }))),
980 ),
981 )
982 .await;
983
984 use actix_http::body::MessageBody as _;
986 let req = TestRequest::with_uri("/app/test").to_request();
987 let resp = call_service(&srv, req).await;
988 let body = resp.into_body();
989 assert_eq!(body.try_into_bytes().unwrap(), b"hello".as_ref());
990 }
991
992 #[actix_rt::test]
993 async fn test_middleware_fn() {
994 let srv = init_service(
995 App::new().service(
996 web::scope("app")
997 .wrap_fn(|req, srv| {
998 let fut = srv.call(req);
999 async move {
1000 let mut res = fut.await?;
1001 res.headers_mut()
1002 .insert(header::CONTENT_TYPE, HeaderValue::from_static("0001"));
1003 Ok(res)
1004 }
1005 })
1006 .route("/test", web::get().to(HttpResponse::Ok)),
1007 ),
1008 )
1009 .await;
1010
1011 let req = TestRequest::with_uri("/app/test").to_request();
1012 let resp = call_service(&srv, req).await;
1013 assert_eq!(resp.status(), StatusCode::OK);
1014 assert_eq!(
1015 resp.headers().get(header::CONTENT_TYPE).unwrap(),
1016 HeaderValue::from_static("0001")
1017 );
1018 }
1019
1020 #[actix_rt::test]
1021 async fn test_middleware_app_data() {
1022 let srv = init_service(
1023 App::new().service(
1024 web::scope("app")
1025 .app_data(1usize)
1026 .wrap_fn(|req, srv| {
1027 assert_eq!(req.app_data::<usize>(), Some(&1usize));
1028 req.extensions_mut().insert(1usize);
1029 srv.call(req)
1030 })
1031 .route("/test", web::get().to(HttpResponse::Ok))
1032 .default_service(|req: ServiceRequest| async move {
1033 let (req, _) = req.into_parts();
1034
1035 assert_eq!(req.extensions().get::<usize>(), Some(&1));
1036
1037 Ok(ServiceResponse::new(
1038 req,
1039 HttpResponse::BadRequest().finish(),
1040 ))
1041 }),
1042 ),
1043 )
1044 .await;
1045
1046 let req = TestRequest::with_uri("/app/test").to_request();
1047 let resp = call_service(&srv, req).await;
1048 assert_eq!(resp.status(), StatusCode::OK);
1049
1050 let req = TestRequest::with_uri("/app/default").to_request();
1051 let resp = call_service(&srv, req).await;
1052 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
1053 }
1054
1055 #[allow(deprecated)]
1057 #[actix_rt::test]
1058 async fn test_override_data() {
1059 let srv = init_service(App::new().data(1usize).service(
1060 web::scope("app").data(10usize).route(
1061 "/t",
1062 web::get().to(|data: web::Data<usize>| {
1063 assert_eq!(**data, 10);
1064 HttpResponse::Ok()
1065 }),
1066 ),
1067 ))
1068 .await;
1069
1070 let req = TestRequest::with_uri("/app/t").to_request();
1071 let resp = call_service(&srv, req).await;
1072 assert_eq!(resp.status(), StatusCode::OK);
1073 }
1074
1075 #[allow(deprecated)]
1077 #[actix_rt::test]
1078 async fn test_override_data_default_service() {
1079 let srv =
1080 init_service(App::new().data(1usize).service(
1081 web::scope("app").data(10usize).default_service(web::to(
1082 |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_override_app_data() {
1097 let srv = init_service(App::new().app_data(web::Data::new(1usize)).service(
1098 web::scope("app").app_data(web::Data::new(10usize)).route(
1099 "/t",
1100 web::get().to(|data: web::Data<usize>| {
1101 assert_eq!(**data, 10);
1102 HttpResponse::Ok()
1103 }),
1104 ),
1105 ))
1106 .await;
1107
1108 let req = TestRequest::with_uri("/app/t").to_request();
1109 let resp = call_service(&srv, req).await;
1110 assert_eq!(resp.status(), StatusCode::OK);
1111 }
1112
1113 #[actix_rt::test]
1114 async fn test_scope_config() {
1115 let srv = init_service(App::new().service(web::scope("/app").configure(|s| {
1116 s.route("/path1", web::get().to(HttpResponse::Ok));
1117 })))
1118 .await;
1119
1120 let req = TestRequest::with_uri("/app/path1").to_request();
1121 let resp = srv.call(req).await.unwrap();
1122 assert_eq!(resp.status(), StatusCode::OK);
1123 }
1124
1125 #[actix_rt::test]
1126 async fn test_scope_config_2() {
1127 let srv = init_service(App::new().service(web::scope("/app").configure(|s| {
1128 s.service(web::scope("/v1").configure(|s| {
1129 s.route("/", web::get().to(HttpResponse::Ok));
1130 }));
1131 })))
1132 .await;
1133
1134 let req = TestRequest::with_uri("/app/v1/").to_request();
1135 let resp = srv.call(req).await.unwrap();
1136 assert_eq!(resp.status(), StatusCode::OK);
1137 }
1138
1139 #[actix_rt::test]
1140 async fn test_url_for_external() {
1141 let srv = init_service(App::new().service(web::scope("/app").configure(|s| {
1142 s.service(web::scope("/v1").configure(|s| {
1143 s.external_resource("youtube", "https://youtube.com/watch/{video_id}");
1144 s.route(
1145 "/",
1146 web::get().to(|req: HttpRequest| {
1147 HttpResponse::Ok()
1148 .body(req.url_for("youtube", ["xxxxxx"]).unwrap().to_string())
1149 }),
1150 );
1151 }));
1152 })))
1153 .await;
1154
1155 let req = TestRequest::with_uri("/app/v1/").to_request();
1156 let resp = srv.call(req).await.unwrap();
1157 assert_eq!(resp.status(), StatusCode::OK);
1158 let body = read_body(resp).await;
1159 assert_eq!(body, &b"https://youtube.com/watch/xxxxxx"[..]);
1160 }
1161
1162 #[actix_rt::test]
1163 async fn test_url_for_nested() {
1164 let srv = init_service(App::new().service(web::scope("/a").service(
1165 web::scope("/b").service(web::resource("/c/{stuff}").name("c").route(web::get().to(
1166 |req: HttpRequest| {
1167 HttpResponse::Ok().body(format!("{}", req.url_for("c", ["12345"]).unwrap()))
1168 },
1169 ))),
1170 )))
1171 .await;
1172
1173 let req = TestRequest::with_uri("/a/b/c/test").to_request();
1174 let resp = call_service(&srv, req).await;
1175 assert_eq!(resp.status(), StatusCode::OK);
1176 let body = read_body(resp).await;
1177 assert_eq!(
1178 body,
1179 Bytes::from_static(b"http://localhost:8080/a/b/c/12345")
1180 );
1181 }
1182
1183 #[actix_rt::test]
1184 async fn dynamic_scopes() {
1185 let srv = init_service(
1186 App::new().service(
1187 web::scope("/{a}/").service(
1188 web::scope("/{b}/")
1189 .route("", web::get().to(|_: HttpRequest| HttpResponse::Created()))
1190 .route(
1191 "/",
1192 web::get().to(|_: HttpRequest| HttpResponse::Accepted()),
1193 )
1194 .route("/{c}", web::get().to(|_: HttpRequest| HttpResponse::Ok())),
1195 ),
1196 ),
1197 )
1198 .await;
1199
1200 let req = TestRequest::with_uri("/a//b//c").to_request();
1202 let resp = call_service(&srv, req).await;
1203 assert_eq!(resp.status(), StatusCode::OK);
1204
1205 let req = TestRequest::with_uri("/a//b/").to_request();
1206 let resp = call_service(&srv, req).await;
1207 assert_eq!(resp.status(), StatusCode::CREATED);
1208
1209 let req = TestRequest::with_uri("/a//b//").to_request();
1210 let resp = call_service(&srv, req).await;
1211 assert_eq!(resp.status(), StatusCode::ACCEPTED);
1212
1213 let req = TestRequest::with_uri("/a//b//c/d").to_request();
1214 let resp = call_service(&srv, req).await;
1215 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
1216
1217 let srv = init_service(
1218 App::new().service(
1219 web::scope("/{a}").service(
1220 web::scope("/{b}")
1221 .route("", web::get().to(|_: HttpRequest| HttpResponse::Created()))
1222 .route(
1223 "/",
1224 web::get().to(|_: HttpRequest| HttpResponse::Accepted()),
1225 )
1226 .route("/{c}", web::get().to(|_: HttpRequest| HttpResponse::Ok())),
1227 ),
1228 ),
1229 )
1230 .await;
1231
1232 let req = TestRequest::with_uri("/a/b/c").to_request();
1233 let resp = call_service(&srv, req).await;
1234 assert_eq!(resp.status(), StatusCode::OK);
1235
1236 let req = TestRequest::with_uri("/a/b").to_request();
1237 let resp = call_service(&srv, req).await;
1238 assert_eq!(resp.status(), StatusCode::CREATED);
1239
1240 let req = TestRequest::with_uri("/a/b/").to_request();
1241 let resp = call_service(&srv, req).await;
1242 assert_eq!(resp.status(), StatusCode::ACCEPTED);
1243
1244 let req = TestRequest::with_uri("/a/b/c/d").to_request();
1245 let resp = call_service(&srv, req).await;
1246 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
1247 }
1248}