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 req.push_resource_id(info.0);
538
539 let matched = req
540 .resource_map()
541 .is_resource_path_match(req.resource_id_path());
542
543 req.mark_resource_path(matched);
544
545 srv.call(req)
546 } else {
547 self.default.call(req)
548 }
549 }
550}
551
552#[doc(hidden)]
553pub struct ScopeEndpoint {
554 factory: Rc<RefCell<Option<ScopeFactory>>>,
555}
556
557impl ScopeEndpoint {
558 fn new(factory: Rc<RefCell<Option<ScopeFactory>>>) -> Self {
559 ScopeEndpoint { factory }
560 }
561}
562
563impl ServiceFactory<ServiceRequest> for ScopeEndpoint {
564 type Response = ServiceResponse;
565 type Error = Error;
566 type Config = ();
567 type Service = ScopeService;
568 type InitError = ();
569 type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
570
571 fn new_service(&self, _: ()) -> Self::Future {
572 self.factory.borrow_mut().as_mut().unwrap().new_service(())
573 }
574}
575
576#[cfg(test)]
577mod tests {
578 use std::future::ready;
579
580 use bytes::Bytes;
581
582 use super::*;
583 use crate::{
584 guard,
585 http::{
586 header::{self, HeaderValue},
587 Method, StatusCode,
588 },
589 middleware::DefaultHeaders,
590 test::{assert_body_eq, call_service, init_service, read_body, TestRequest},
591 web, App, HttpMessage, HttpRequest, HttpResponse,
592 };
593
594 #[test]
595 fn can_be_returned_from_fn() {
596 fn my_scope_1() -> Scope {
597 web::scope("/test")
598 .service(web::resource("").route(web::get().to(|| async { "hello" })))
599 }
600
601 fn my_scope_2() -> Scope<
602 impl ServiceFactory<
603 ServiceRequest,
604 Config = (),
605 Response = ServiceResponse<impl MessageBody>,
606 Error = Error,
607 InitError = (),
608 >,
609 > {
610 web::scope("/test-compat")
611 .wrap_fn(|req, srv| {
612 let fut = srv.call(req);
613 async { Ok(fut.await?.map_into_right_body::<()>()) }
614 })
615 .service(web::resource("").route(web::get().to(|| async { "hello" })))
616 }
617
618 fn my_scope_3() -> impl HttpServiceFactory {
619 my_scope_2()
620 }
621
622 App::new()
623 .service(my_scope_1())
624 .service(my_scope_2())
625 .service(my_scope_3());
626 }
627
628 #[actix_rt::test]
629 async fn test_scope() {
630 let srv = init_service(
631 App::new()
632 .service(web::scope("/app").service(web::resource("/path1").to(HttpResponse::Ok))),
633 )
634 .await;
635
636 let req = TestRequest::with_uri("/app/path1").to_request();
637 let resp = srv.call(req).await.unwrap();
638 assert_eq!(resp.status(), StatusCode::OK);
639 }
640
641 #[actix_rt::test]
642 async fn test_scope_root() {
643 let srv = init_service(
644 App::new().service(
645 web::scope("/app")
646 .service(web::resource("").to(HttpResponse::Ok))
647 .service(web::resource("/").to(HttpResponse::Created)),
648 ),
649 )
650 .await;
651
652 let req = TestRequest::with_uri("/app").to_request();
653 let resp = srv.call(req).await.unwrap();
654 assert_eq!(resp.status(), StatusCode::OK);
655
656 let req = TestRequest::with_uri("/app/").to_request();
657 let resp = srv.call(req).await.unwrap();
658 assert_eq!(resp.status(), StatusCode::CREATED);
659 }
660
661 #[actix_rt::test]
662 async fn test_scope_root2() {
663 let srv = init_service(
664 App::new().service(web::scope("/app/").service(web::resource("").to(HttpResponse::Ok))),
665 )
666 .await;
667
668 let req = TestRequest::with_uri("/app").to_request();
669 let resp = srv.call(req).await.unwrap();
670 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
671
672 let req = TestRequest::with_uri("/app/").to_request();
673 let resp = srv.call(req).await.unwrap();
674 assert_eq!(resp.status(), StatusCode::OK);
675 }
676
677 #[actix_rt::test]
678 async fn test_scope_root3() {
679 let srv = init_service(
680 App::new()
681 .service(web::scope("/app/").service(web::resource("/").to(HttpResponse::Ok))),
682 )
683 .await;
684
685 let req = TestRequest::with_uri("/app").to_request();
686 let resp = srv.call(req).await.unwrap();
687 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
688
689 let req = TestRequest::with_uri("/app/").to_request();
690 let resp = srv.call(req).await.unwrap();
691 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
692 }
693
694 #[actix_rt::test]
695 async fn test_scope_route() {
696 let srv = init_service(
697 App::new().service(
698 web::scope("app")
699 .route("/path1", web::get().to(HttpResponse::Ok))
700 .route("/path1", web::delete().to(HttpResponse::Ok)),
701 ),
702 )
703 .await;
704
705 let req = TestRequest::with_uri("/app/path1").to_request();
706 let resp = srv.call(req).await.unwrap();
707 assert_eq!(resp.status(), StatusCode::OK);
708
709 let req = TestRequest::with_uri("/app/path1")
710 .method(Method::DELETE)
711 .to_request();
712 let resp = srv.call(req).await.unwrap();
713 assert_eq!(resp.status(), StatusCode::OK);
714
715 let req = TestRequest::with_uri("/app/path1")
716 .method(Method::POST)
717 .to_request();
718 let resp = srv.call(req).await.unwrap();
719 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
720 }
721
722 #[actix_rt::test]
723 async fn test_scope_route_without_leading_slash() {
724 let srv = init_service(
725 App::new().service(
726 web::scope("app").service(
727 web::resource("path1")
728 .route(web::get().to(HttpResponse::Ok))
729 .route(web::delete().to(HttpResponse::Ok)),
730 ),
731 ),
732 )
733 .await;
734
735 let req = TestRequest::with_uri("/app/path1").to_request();
736 let resp = srv.call(req).await.unwrap();
737 assert_eq!(resp.status(), StatusCode::OK);
738
739 let req = TestRequest::with_uri("/app/path1")
740 .method(Method::DELETE)
741 .to_request();
742 let resp = srv.call(req).await.unwrap();
743 assert_eq!(resp.status(), StatusCode::OK);
744
745 let req = TestRequest::with_uri("/app/path1")
746 .method(Method::POST)
747 .to_request();
748 let resp = srv.call(req).await.unwrap();
749 assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
750 }
751
752 #[actix_rt::test]
753 async fn test_scope_guard() {
754 let srv = init_service(
755 App::new().service(
756 web::scope("/app")
757 .guard(guard::Get())
758 .service(web::resource("/path1").to(HttpResponse::Ok)),
759 ),
760 )
761 .await;
762
763 let req = TestRequest::with_uri("/app/path1")
764 .method(Method::POST)
765 .to_request();
766 let resp = srv.call(req).await.unwrap();
767 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
768
769 let req = TestRequest::with_uri("/app/path1")
770 .method(Method::GET)
771 .to_request();
772 let resp = srv.call(req).await.unwrap();
773 assert_eq!(resp.status(), StatusCode::OK);
774 }
775
776 #[actix_rt::test]
777 async fn test_scope_variable_segment() {
778 let srv = init_service(App::new().service(web::scope("/ab-{project}").service(
779 web::resource("/path1").to(|r: HttpRequest| {
780 HttpResponse::Ok().body(format!("project: {}", &r.match_info()["project"]))
781 }),
782 )))
783 .await;
784
785 let req = TestRequest::with_uri("/ab-project1/path1").to_request();
786 let res = srv.call(req).await.unwrap();
787 assert_eq!(res.status(), StatusCode::OK);
788 assert_body_eq!(res, b"project: project1");
789
790 let req = TestRequest::with_uri("/aa-project1/path1").to_request();
791 let res = srv.call(req).await.unwrap();
792 assert_eq!(res.status(), StatusCode::NOT_FOUND);
793 }
794
795 #[actix_rt::test]
796 async fn test_nested_scope() {
797 let srv = init_service(App::new().service(web::scope("/app").service(
798 web::scope("/t1").service(web::resource("/path1").to(HttpResponse::Created)),
799 )))
800 .await;
801
802 let req = TestRequest::with_uri("/app/t1/path1").to_request();
803 let resp = srv.call(req).await.unwrap();
804 assert_eq!(resp.status(), StatusCode::CREATED);
805 }
806
807 #[actix_rt::test]
808 async fn test_nested_scope_no_slash() {
809 let srv =
810 init_service(App::new().service(web::scope("/app").service(
811 web::scope("t1").service(web::resource("/path1").to(HttpResponse::Created)),
812 )))
813 .await;
814
815 let req = TestRequest::with_uri("/app/t1/path1").to_request();
816 let resp = srv.call(req).await.unwrap();
817 assert_eq!(resp.status(), StatusCode::CREATED);
818 }
819
820 #[actix_rt::test]
821 async fn test_nested_scope_root() {
822 let srv = init_service(
823 App::new().service(
824 web::scope("/app").service(
825 web::scope("/t1")
826 .service(web::resource("").to(HttpResponse::Ok))
827 .service(web::resource("/").to(HttpResponse::Created)),
828 ),
829 ),
830 )
831 .await;
832
833 let req = TestRequest::with_uri("/app/t1").to_request();
834 let resp = srv.call(req).await.unwrap();
835 assert_eq!(resp.status(), StatusCode::OK);
836
837 let req = TestRequest::with_uri("/app/t1/").to_request();
838 let resp = srv.call(req).await.unwrap();
839 assert_eq!(resp.status(), StatusCode::CREATED);
840 }
841
842 #[actix_rt::test]
843 async fn test_nested_scope_filter() {
844 let srv = init_service(
845 App::new().service(
846 web::scope("/app").service(
847 web::scope("/t1")
848 .guard(guard::Get())
849 .service(web::resource("/path1").to(HttpResponse::Ok)),
850 ),
851 ),
852 )
853 .await;
854
855 let req = TestRequest::with_uri("/app/t1/path1")
856 .method(Method::POST)
857 .to_request();
858 let resp = srv.call(req).await.unwrap();
859 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
860
861 let req = TestRequest::with_uri("/app/t1/path1")
862 .method(Method::GET)
863 .to_request();
864 let resp = srv.call(req).await.unwrap();
865 assert_eq!(resp.status(), StatusCode::OK);
866 }
867
868 #[actix_rt::test]
869 async fn test_nested_scope_with_variable_segment() {
870 let srv = init_service(App::new().service(web::scope("/app").service(
871 web::scope("/{project_id}").service(web::resource("/path1").to(|r: HttpRequest| {
872 HttpResponse::Created().body(format!("project: {}", &r.match_info()["project_id"]))
873 })),
874 )))
875 .await;
876
877 let req = TestRequest::with_uri("/app/project_1/path1").to_request();
878 let res = srv.call(req).await.unwrap();
879 assert_eq!(res.status(), StatusCode::CREATED);
880 assert_body_eq!(res, b"project: project_1");
881 }
882
883 #[actix_rt::test]
884 async fn test_nested2_scope_with_variable_segment() {
885 let srv = init_service(App::new().service(web::scope("/app").service(
886 web::scope("/{project}").service(web::scope("/{id}").service(
887 web::resource("/path1").to(|r: HttpRequest| {
888 HttpResponse::Created().body(format!(
889 "project: {} - {}",
890 &r.match_info()["project"],
891 &r.match_info()["id"],
892 ))
893 }),
894 )),
895 )))
896 .await;
897
898 let req = TestRequest::with_uri("/app/test/1/path1").to_request();
899 let res = srv.call(req).await.unwrap();
900 assert_eq!(res.status(), StatusCode::CREATED);
901 assert_body_eq!(res, b"project: test - 1");
902
903 let req = TestRequest::with_uri("/app/test/1/path2").to_request();
904 let res = srv.call(req).await.unwrap();
905 assert_eq!(res.status(), StatusCode::NOT_FOUND);
906 }
907
908 #[actix_rt::test]
909 async fn test_default_resource() {
910 let srv = init_service(
911 App::new().service(
912 web::scope("/app")
913 .service(web::resource("/path1").to(HttpResponse::Ok))
914 .default_service(|r: ServiceRequest| {
915 ready(Ok(r.into_response(HttpResponse::BadRequest())))
916 }),
917 ),
918 )
919 .await;
920
921 let req = TestRequest::with_uri("/app/path2").to_request();
922 let resp = srv.call(req).await.unwrap();
923 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
924
925 let req = TestRequest::with_uri("/path2").to_request();
926 let resp = srv.call(req).await.unwrap();
927 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
928 }
929
930 #[actix_rt::test]
931 async fn test_default_resource_propagation() {
932 let srv = init_service(
933 App::new()
934 .service(web::scope("/app1").default_service(web::to(HttpResponse::BadRequest)))
935 .service(web::scope("/app2"))
936 .default_service(|r: ServiceRequest| {
937 ready(Ok(r.into_response(HttpResponse::MethodNotAllowed())))
938 }),
939 )
940 .await;
941
942 let req = TestRequest::with_uri("/non-exist").to_request();
943 let resp = srv.call(req).await.unwrap();
944 assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
945
946 let req = TestRequest::with_uri("/app1/non-exist").to_request();
947 let resp = srv.call(req).await.unwrap();
948 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
949
950 let req = TestRequest::with_uri("/app2/non-exist").to_request();
951 let resp = srv.call(req).await.unwrap();
952 assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
953 }
954
955 #[actix_rt::test]
956 async fn test_middleware() {
957 let srv = init_service(
958 App::new().service(
959 web::scope("app")
960 .wrap(
961 DefaultHeaders::new()
962 .add((header::CONTENT_TYPE, HeaderValue::from_static("0001"))),
963 )
964 .service(web::resource("/test").route(web::get().to(HttpResponse::Ok))),
965 ),
966 )
967 .await;
968
969 let req = TestRequest::with_uri("/app/test").to_request();
970 let resp = call_service(&srv, req).await;
971 assert_eq!(resp.status(), StatusCode::OK);
972 assert_eq!(
973 resp.headers().get(header::CONTENT_TYPE).unwrap(),
974 HeaderValue::from_static("0001")
975 );
976 }
977
978 #[actix_rt::test]
979 async fn test_middleware_body_type() {
980 let srv = init_service(
982 App::new().service(
983 web::scope("app")
984 .wrap_fn(|req, srv| {
985 let fut = srv.call(req);
986 async { Ok(fut.await?.map_into_right_body::<()>()) }
987 })
988 .service(web::resource("/test").route(web::get().to(|| async { "hello" }))),
989 ),
990 )
991 .await;
992
993 use actix_http::body::MessageBody as _;
995 let req = TestRequest::with_uri("/app/test").to_request();
996 let resp = call_service(&srv, req).await;
997 let body = resp.into_body();
998 assert_eq!(body.try_into_bytes().unwrap(), b"hello".as_ref());
999 }
1000
1001 #[actix_rt::test]
1002 async fn test_middleware_fn() {
1003 let srv = init_service(
1004 App::new().service(
1005 web::scope("app")
1006 .wrap_fn(|req, srv| {
1007 let fut = srv.call(req);
1008 async move {
1009 let mut res = fut.await?;
1010 res.headers_mut()
1011 .insert(header::CONTENT_TYPE, HeaderValue::from_static("0001"));
1012 Ok(res)
1013 }
1014 })
1015 .route("/test", web::get().to(HttpResponse::Ok)),
1016 ),
1017 )
1018 .await;
1019
1020 let req = TestRequest::with_uri("/app/test").to_request();
1021 let resp = call_service(&srv, req).await;
1022 assert_eq!(resp.status(), StatusCode::OK);
1023 assert_eq!(
1024 resp.headers().get(header::CONTENT_TYPE).unwrap(),
1025 HeaderValue::from_static("0001")
1026 );
1027 }
1028
1029 #[actix_rt::test]
1030 async fn test_middleware_app_data() {
1031 let srv = init_service(
1032 App::new().service(
1033 web::scope("app")
1034 .app_data(1usize)
1035 .wrap_fn(|req, srv| {
1036 assert_eq!(req.app_data::<usize>(), Some(&1usize));
1037 req.extensions_mut().insert(1usize);
1038 srv.call(req)
1039 })
1040 .route("/test", web::get().to(HttpResponse::Ok))
1041 .default_service(|req: ServiceRequest| async move {
1042 let (req, _) = req.into_parts();
1043
1044 assert_eq!(req.extensions().get::<usize>(), Some(&1));
1045
1046 Ok(ServiceResponse::new(
1047 req,
1048 HttpResponse::BadRequest().finish(),
1049 ))
1050 }),
1051 ),
1052 )
1053 .await;
1054
1055 let req = TestRequest::with_uri("/app/test").to_request();
1056 let resp = call_service(&srv, req).await;
1057 assert_eq!(resp.status(), StatusCode::OK);
1058
1059 let req = TestRequest::with_uri("/app/default").to_request();
1060 let resp = call_service(&srv, req).await;
1061 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
1062 }
1063
1064 #[allow(deprecated)]
1066 #[actix_rt::test]
1067 async fn test_override_data() {
1068 let srv = init_service(App::new().data(1usize).service(
1069 web::scope("app").data(10usize).route(
1070 "/t",
1071 web::get().to(|data: web::Data<usize>| {
1072 assert_eq!(**data, 10);
1073 HttpResponse::Ok()
1074 }),
1075 ),
1076 ))
1077 .await;
1078
1079 let req = TestRequest::with_uri("/app/t").to_request();
1080 let resp = call_service(&srv, req).await;
1081 assert_eq!(resp.status(), StatusCode::OK);
1082 }
1083
1084 #[allow(deprecated)]
1086 #[actix_rt::test]
1087 async fn test_override_data_default_service() {
1088 let srv =
1089 init_service(App::new().data(1usize).service(
1090 web::scope("app").data(10usize).default_service(web::to(
1091 |data: web::Data<usize>| {
1092 assert_eq!(**data, 10);
1093 HttpResponse::Ok()
1094 },
1095 )),
1096 ))
1097 .await;
1098
1099 let req = TestRequest::with_uri("/app/t").to_request();
1100 let resp = call_service(&srv, req).await;
1101 assert_eq!(resp.status(), StatusCode::OK);
1102 }
1103
1104 #[actix_rt::test]
1105 async fn test_override_app_data() {
1106 let srv = init_service(App::new().app_data(web::Data::new(1usize)).service(
1107 web::scope("app").app_data(web::Data::new(10usize)).route(
1108 "/t",
1109 web::get().to(|data: web::Data<usize>| {
1110 assert_eq!(**data, 10);
1111 HttpResponse::Ok()
1112 }),
1113 ),
1114 ))
1115 .await;
1116
1117 let req = TestRequest::with_uri("/app/t").to_request();
1118 let resp = call_service(&srv, req).await;
1119 assert_eq!(resp.status(), StatusCode::OK);
1120 }
1121
1122 #[actix_rt::test]
1123 async fn test_scope_config() {
1124 let srv = init_service(App::new().service(web::scope("/app").configure(|s| {
1125 s.route("/path1", web::get().to(HttpResponse::Ok));
1126 })))
1127 .await;
1128
1129 let req = TestRequest::with_uri("/app/path1").to_request();
1130 let resp = srv.call(req).await.unwrap();
1131 assert_eq!(resp.status(), StatusCode::OK);
1132 }
1133
1134 #[actix_rt::test]
1135 async fn test_scope_config_2() {
1136 let srv = init_service(App::new().service(web::scope("/app").configure(|s| {
1137 s.service(web::scope("/v1").configure(|s| {
1138 s.route("/", web::get().to(HttpResponse::Ok));
1139 }));
1140 })))
1141 .await;
1142
1143 let req = TestRequest::with_uri("/app/v1/").to_request();
1144 let resp = srv.call(req).await.unwrap();
1145 assert_eq!(resp.status(), StatusCode::OK);
1146 }
1147
1148 #[actix_rt::test]
1149 async fn test_url_for_external() {
1150 let srv = init_service(App::new().service(web::scope("/app").configure(|s| {
1151 s.service(web::scope("/v1").configure(|s| {
1152 s.external_resource("youtube", "https://youtube.com/watch/{video_id}");
1153 s.route(
1154 "/",
1155 web::get().to(|req: HttpRequest| {
1156 HttpResponse::Ok()
1157 .body(req.url_for("youtube", ["xxxxxx"]).unwrap().to_string())
1158 }),
1159 );
1160 }));
1161 })))
1162 .await;
1163
1164 let req = TestRequest::with_uri("/app/v1/").to_request();
1165 let resp = srv.call(req).await.unwrap();
1166 assert_eq!(resp.status(), StatusCode::OK);
1167 let body = read_body(resp).await;
1168 assert_eq!(body, &b"https://youtube.com/watch/xxxxxx"[..]);
1169 }
1170
1171 #[actix_rt::test]
1172 async fn test_url_for_nested() {
1173 let srv = init_service(App::new().service(web::scope("/a").service(
1174 web::scope("/b").service(web::resource("/c/{stuff}").name("c").route(web::get().to(
1175 |req: HttpRequest| {
1176 HttpResponse::Ok().body(format!("{}", req.url_for("c", ["12345"]).unwrap()))
1177 },
1178 ))),
1179 )))
1180 .await;
1181
1182 let req = TestRequest::with_uri("/a/b/c/test").to_request();
1183 let resp = call_service(&srv, req).await;
1184 assert_eq!(resp.status(), StatusCode::OK);
1185 let body = read_body(resp).await;
1186 assert_eq!(
1187 body,
1188 Bytes::from_static(b"http://localhost:8080/a/b/c/12345")
1189 );
1190 }
1191
1192 #[actix_rt::test]
1193 async fn dynamic_scopes() {
1194 let srv = init_service(
1195 App::new().service(
1196 web::scope("/{a}/").service(
1197 web::scope("/{b}/")
1198 .route("", web::get().to(|_: HttpRequest| HttpResponse::Created()))
1199 .route(
1200 "/",
1201 web::get().to(|_: HttpRequest| HttpResponse::Accepted()),
1202 )
1203 .route("/{c}", web::get().to(|_: HttpRequest| HttpResponse::Ok())),
1204 ),
1205 ),
1206 )
1207 .await;
1208
1209 let req = TestRequest::with_uri("/a//b//c").to_request();
1211 let resp = call_service(&srv, req).await;
1212 assert_eq!(resp.status(), StatusCode::OK);
1213
1214 let req = TestRequest::with_uri("/a//b/").to_request();
1215 let resp = call_service(&srv, req).await;
1216 assert_eq!(resp.status(), StatusCode::CREATED);
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::ACCEPTED);
1221
1222 let req = TestRequest::with_uri("/a//b//c/d").to_request();
1223 let resp = call_service(&srv, req).await;
1224 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
1225
1226 let srv = init_service(
1227 App::new().service(
1228 web::scope("/{a}").service(
1229 web::scope("/{b}")
1230 .route("", web::get().to(|_: HttpRequest| HttpResponse::Created()))
1231 .route(
1232 "/",
1233 web::get().to(|_: HttpRequest| HttpResponse::Accepted()),
1234 )
1235 .route("/{c}", web::get().to(|_: HttpRequest| HttpResponse::Ok())),
1236 ),
1237 ),
1238 )
1239 .await;
1240
1241 let req = TestRequest::with_uri("/a/b/c").to_request();
1242 let resp = call_service(&srv, req).await;
1243 assert_eq!(resp.status(), StatusCode::OK);
1244
1245 let req = TestRequest::with_uri("/a/b").to_request();
1246 let resp = call_service(&srv, req).await;
1247 assert_eq!(resp.status(), StatusCode::CREATED);
1248
1249 let req = TestRequest::with_uri("/a/b/").to_request();
1250 let resp = call_service(&srv, req).await;
1251 assert_eq!(resp.status(), StatusCode::ACCEPTED);
1252
1253 let req = TestRequest::with_uri("/a/b/c/d").to_request();
1254 let resp = call_service(&srv, req).await;
1255 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
1256 }
1257}