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