1use std::marker::PhantomData;
2
3use crate::error::{Failure, IntoFailure};
4use crate::http::Response;
5use crate::router::{IntoPattern, ResourceDef};
6use crate::service::{Identity, ServiceChainFactory};
7use crate::{Ctx, IntoServiceFactory, Middleware, Service, ServiceFactory, factory};
8
9use super::dev::{WebServiceConfig, WebServiceFactory, insert_slash};
10use super::error::{WebError, WebResponseError};
11use super::guard::Guard;
12use super::route::{IntoRoutes, Route, RouteService};
13use super::stack::{Filter, WebStack};
14use super::{
15 FromRequest, Handler, HandlerSt, HttpHandler, HttpService, State, WebRequest, WebResponse,
16};
17
18#[derive(derive_more::Debug)]
54#[debug("Resource({rdef:?})")]
55pub struct Resource<St: State, In, Out = In, M = Identity, F = Filter<St, In>> {
56 middleware: M,
57 filter: ServiceChainFactory<F, St, WebRequest<In>>,
58 rdef: Vec<String>,
59 name: Option<String>,
60 guards: Vec<Box<dyn Guard>>,
61 ph: PhantomData<Out>,
62}
63
64#[derive(derive_more::Debug)]
77#[debug("ResourceServices({rdef:?})")]
78pub struct ResourceServices<St: State, In, Out, M, F> {
79 middleware: M,
80 filter: ServiceChainFactory<F, St, WebRequest<In>>,
81 rdef: Vec<String>,
82 name: Option<String>,
83 guards: Vec<Box<dyn Guard>>,
84 routes: Vec<Route<St, Out>>,
85 default: Option<HttpService<St, Out>>,
86}
87
88impl<St: State, In: 'static> Resource<St, In, In> {
89 #[allow(clippy::needless_pass_by_value)]
90 pub fn new<T: IntoPattern>(path: T) -> Resource<St, In, In> {
94 Resource {
95 rdef: path.patterns(),
96 name: None,
97 middleware: Identity,
98 filter: factory(Filter::new()),
99 guards: Vec::new(),
100 ph: PhantomData,
101 }
102 }
103}
104
105impl<St, In, Out, M, F> Resource<St, In, Out, M, F>
106where
107 St: State,
108 In: 'static,
109 Out: 'static,
110 F: ServiceFactory<
111 St,
112 WebRequest<In>,
113 Res = WebRequest<Out>,
114 Error = WebError<St, St::Error>,
115 InitError = Failure,
116 >,
117{
118 #[must_use]
122 pub fn name(mut self, name: &str) -> Self {
123 self.name = Some(name.to_string());
124 self
125 }
126
127 #[must_use]
153 pub fn guard<G: Guard + 'static>(mut self, guard: G) -> Self {
154 self.guards.push(Box::new(guard));
155 self
156 }
157
158 pub(crate) fn add_guards(mut self, guards: Vec<Box<dyn Guard>>) -> Self {
159 self.guards.extend(guards);
160 self
161 }
162
163 #[must_use]
186 pub fn filter<U, R>(
187 self,
188 filter: impl IntoServiceFactory<U, St, WebRequest<Out>>,
189 ) -> Resource<
190 St,
191 In,
192 R,
193 M,
194 impl ServiceFactory<
195 St,
196 WebRequest<In>,
197 Res = WebRequest<R>,
198 Error = WebError<St, St::Error>,
199 InitError = Failure,
200 >,
201 >
202 where
203 U: ServiceFactory<St, WebRequest<Out>, Res = WebRequest<R>>,
204 U::Error: WebResponseError<St, St::Error>,
205 U::InitError: IntoFailure,
206 {
207 Resource {
208 filter: self.filter.and_then(
209 filter
210 .into_factory()
211 .map_err(WebError::from_err)
212 .map_init_err(IntoFailure::fail),
213 ),
214 middleware: self.middleware,
215 rdef: self.rdef,
216 name: self.name,
217 guards: self.guards,
218 ph: PhantomData,
219 }
220 }
221
222 #[must_use]
239 pub fn middleware<U>(self, mw: U) -> Resource<St, In, Out, WebStack<St, U, M>, F> {
240 Resource {
241 middleware: WebStack::new(mw, self.middleware),
242 filter: self.filter,
243 rdef: self.rdef,
244 name: self.name,
245 guards: self.guards,
246 ph: PhantomData,
247 }
248 }
249
250 #[must_use]
272 pub fn route<R>(self, route: R) -> ResourceServices<St, In, Out, M, F>
273 where
274 R: IntoRoutes<St, Out>,
275 {
276 let mut routes = Vec::new();
277 for route in route.routes() {
278 routes.push(route);
279 }
280
281 ResourceServices {
282 routes,
283 name: self.name,
284 rdef: self.rdef,
285 guards: self.guards,
286 filter: self.filter,
287 middleware: self.middleware,
288 default: None,
289 }
290 }
291
292 #[must_use]
311 pub fn to<Args>(self, h: impl Handler<St, Args>) -> ResourceServices<St, In, Out, M, F>
312 where
313 Args: FromRequest<St> + 'static,
314 Args::Error: WebResponseError<St, St::Error>,
315 {
316 ResourceServices {
317 name: self.name,
318 rdef: self.rdef,
319 guards: self.guards,
320 filter: self.filter,
321 middleware: self.middleware,
322 default: None,
323 routes: vec![Route::new().to(h)],
324 }
325 }
326
327 #[must_use]
358 pub fn to_with_state<Args>(
359 self,
360 h: impl HandlerSt<St, Out, Args>,
361 ) -> ResourceServices<St, In, Out, M, F>
362 where
363 Args: FromRequest<St> + 'static,
364 Args::Error: WebResponseError<St, St::Error>,
365 {
366 ResourceServices {
367 name: self.name,
368 rdef: self.rdef,
369 guards: self.guards,
370 filter: self.filter,
371 middleware: self.middleware,
372 default: None,
373 routes: vec![Route::new().to_with_state(h)],
374 }
375 }
376
377 #[must_use]
397 pub fn default_service<S>(
398 self,
399 f: impl IntoServiceFactory<S, St, WebRequest<Out>>,
400 ) -> ResourceServices<St, In, Out, M, F>
401 where
402 S: ServiceFactory<St, WebRequest<Out>, Res = WebResponse> + 'static,
403 S::Error: WebResponseError<St, St::Error>,
404 S::InitError: IntoFailure,
405 {
406 ResourceServices {
408 name: self.name,
409 rdef: self.rdef,
410 guards: self.guards,
411 filter: self.filter,
412 routes: Vec::new(),
413 middleware: self.middleware,
414 default: Some(HttpService::new(
415 f.into_factory()
416 .map_err(WebError::from_err)
417 .map_init_err(IntoFailure::fail),
418 )),
419 }
420 }
421}
422
423impl<St, In, Out, M, F> ResourceServices<St, In, Out, M, F>
424where
425 St: State,
426 In: 'static,
427 Out: 'static,
428 M: 'static,
429 F: ServiceFactory<
430 St,
431 WebRequest<In>,
432 Res = WebRequest<Out>,
433 Error = WebError<St, St::Error>,
434 InitError = Failure,
435 >,
436{
437 #[must_use]
460 pub fn route<R>(mut self, route: R) -> Self
461 where
462 R: IntoRoutes<St, Out>,
463 {
464 for route in route.routes() {
465 self.routes.push(route);
466 }
467 self
468 }
469
470 #[must_use]
489 pub fn to<Args>(mut self, handler: impl Handler<St, Args>) -> Self
490 where
491 Args: FromRequest<St> + 'static,
492 Args::Error: WebResponseError<St, St::Error>,
493 {
494 self.routes.push(Route::new().to(handler));
495 self
496 }
497
498 #[must_use]
529 pub fn to_with_state<Args>(mut self, handler: impl HandlerSt<St, Out, Args>) -> Self
530 where
531 Args: FromRequest<St> + 'static,
532 Args::Error: WebResponseError<St, St::Error>,
533 {
534 self.routes.push(Route::new().to_with_state(handler));
535 self
536 }
537
538 #[must_use]
558 pub fn default_service<S>(mut self, f: impl IntoServiceFactory<S, St, WebRequest<Out>>) -> Self
559 where
560 S: ServiceFactory<St, WebRequest<Out>, Res = WebResponse> + 'static,
561 S::Error: WebResponseError<St, St::Error>,
562 S::InitError: IntoFailure,
563 {
564 self.default = Some(HttpService::new(
566 f.into_factory()
567 .map_err(WebError::from_err)
568 .map_init_err(IntoFailure::fail),
569 ));
570
571 self
572 }
573}
574
575impl<St, Outer, In, Out, M, F> WebServiceFactory<St, Outer> for ResourceServices<St, In, Out, M, F>
576where
577 St: State,
578 Outer: 'static,
579 In: 'static,
580 Out: 'static,
581 F: ServiceFactory<
582 St,
583 WebRequest<In>,
584 Res = WebRequest<Out>,
585 Error = WebError<St, St::Error>,
586 InitError = Failure,
587 > + 'static,
588 M: Middleware<ResourceService<St, In, Out, F::Service>, St> + 'static,
589 M::Service: Service<St, WebRequest<Outer>, Res = WebResponse, Error = WebError<St, St::Error>>,
590{
591 fn register(mut self, config: &mut WebServiceConfig<St, Outer>) {
592 let guards = if self.guards.is_empty() {
593 None
594 } else {
595 Some(std::mem::take(&mut self.guards))
596 };
597 let mut rdef = if config.is_root() || !self.rdef.is_empty() {
598 ResourceDef::new(insert_slash(self.rdef.clone()))
599 } else {
600 ResourceDef::new(self.rdef.clone())
601 };
602 if let Some(ref name) = self.name {
603 rdef.set_name(name.clone());
604 }
605
606 config.register_service(
607 rdef,
608 guards,
609 None,
610 ResourceServiceFactory {
611 middleware: self.middleware,
612 filter: self.filter,
613 routes: self.routes,
614 default: self.default.take(),
615 ph: PhantomData,
616 },
617 );
618 }
619}
620
621impl<St, Outer, In, Out, M, F>
622 IntoServiceFactory<
623 ResourceServiceFactory<St, In, Out, M, ServiceChainFactory<F, St, WebRequest<In>>>,
624 St,
625 WebRequest<Outer>,
626 > for ResourceServices<St, In, Out, M, F>
627where
628 St: State,
629 Outer: 'static,
630 In: 'static,
631 Out: 'static,
632 F: ServiceFactory<
633 St,
634 WebRequest<In>,
635 Res = WebRequest<Out>,
636 Error = WebError<St, St::Error>,
637 InitError = Failure,
638 > + 'static,
639 M: Middleware<ResourceService<St, In, Out, F::Service>, St> + 'static,
640 M::Service: Service<St, WebRequest<Outer>, Res = WebResponse, Error = WebError<St, St::Error>>,
641{
642 fn into_factory(
643 mut self,
644 ) -> ResourceServiceFactory<St, In, Out, M, ServiceChainFactory<F, St, WebRequest<In>>> {
645 ResourceServiceFactory {
646 middleware: self.middleware,
647 filter: self.filter,
648 routes: self.routes,
649 default: self.default.take(),
650 ph: PhantomData,
651 }
652 }
653}
654
655#[derive(derive_more::Debug)]
657#[debug("ResourceServiceFactory")]
658pub struct ResourceServiceFactory<St: State, In, Out, M, F> {
659 middleware: M,
660 filter: F,
661 routes: Vec<Route<St, Out>>,
662 default: Option<HttpService<St, Out>>,
663 ph: PhantomData<In>,
664}
665
666impl<St, Outer, In, Out, M, F> ServiceFactory<St, WebRequest<Outer>>
667 for ResourceServiceFactory<St, In, Out, M, F>
668where
669 St: State,
670 Out: 'static,
671 F: ServiceFactory<
672 St,
673 WebRequest<In>,
674 Res = WebRequest<Out>,
675 Error = WebError<St, St::Error>,
676 InitError = Failure,
677 > + 'static,
678 M: Middleware<ResourceService<St, In, Out, F::Service>, St> + 'static,
679 M::Service: Service<St, WebRequest<Outer>, Res = WebResponse, Error = WebError<St, St::Error>>,
680{
681 type Res = WebResponse;
682 type Error = WebError<St, St::Error>;
683
684 type Service = M::Service;
685 type InitError = Failure;
686
687 async fn create(&self, st: &St) -> Result<Self::Service, Self::InitError> {
688 let filter = self.filter.create(st).await?;
689 let default = if let Some(ref default) = self.default {
690 Some(default.create(st).await?)
691 } else {
692 None
693 };
694
695 Ok(self.middleware.create(
696 st,
697 ResourceService {
698 filter,
699 default,
700 routes: self.routes.iter().map(Route::service).collect(),
701 ph: PhantomData,
702 },
703 ))
704 }
705}
706
707#[derive(derive_more::Debug)]
709#[debug("ResourceService")]
710pub struct ResourceService<St: State, In, Out, F> {
711 filter: F,
712 routes: Vec<RouteService<St, Out>>,
713 default: Option<HttpHandler<St, Out>>,
714 ph: PhantomData<In>,
715}
716
717impl<St, In, Out, F> Service<St, WebRequest<In>> for ResourceService<St, In, Out, F>
718where
719 St: State,
720 F: Service<St, WebRequest<In>, Res = WebRequest<Out>, Error = WebError<St, St::Error>>,
721{
722 type Res = WebResponse;
723 type Error = WebError<St, St::Error>;
724
725 async fn call(
726 &self,
727 req: WebRequest<In>,
728 ctx: Ctx<'_, Self, St>,
729 ) -> Result<Self::Res, Self::Error> {
730 let mut req = ctx.call(&self.filter, req).await?;
731
732 for route in &self.routes {
733 if route.check(&mut req) {
734 return ctx.call(route, req).await;
735 }
736 }
737 if let Some(ref default) = self.default {
738 ctx.call(default, req).await
739 } else {
740 Ok(WebResponse::new(
741 Response::MethodNotAllowed().build(),
742 req.into_parts().0,
743 ))
744 }
745 }
746}
747
748#[cfg(test)]
749mod tests {
750 use std::convert::Infallible;
751
752 use crate::http::{Method, StatusCode};
753 use crate::time::{Millis, sleep};
754 use crate::web::test::{TestRequest, call_service, init_service};
755 use crate::web::{self, App, HttpResponse, guard, request::WebRequest};
756
757 #[crate::rt_test]
758 async fn test_filter() {
759 let filter = std::rc::Rc::new(std::cell::Cell::new(false));
760 let filter2 = filter.clone();
761 let srv = init_service(
762 App::new().service(
763 web::resource("/test")
764 .filter(async move |req: WebRequest<()>| {
765 filter2.set(true);
766 Ok::<_, Infallible>(req)
767 })
768 .route(web::get().to(async || HttpResponse::Ok())),
769 ),
770 )
771 .await;
772 let req = TestRequest::with_uri("/test").to_request();
773 let resp = call_service(&srv, req).await;
774 assert_eq!(resp.status(), StatusCode::OK);
775 assert!(filter.get());
776 }
777
778 #[crate::rt_test]
779 async fn test_to() {
780 let srv = init_service(App::new().service(web::resource("/test").to(async || {
781 sleep(Millis(100)).await;
782 HttpResponse::Ok()
783 })))
784 .await;
785 let req = TestRequest::with_uri("/test").to_request();
786 let resp = call_service(&srv, req).await;
787 assert_eq!(resp.status(), StatusCode::OK);
788 }
789
790 #[crate::rt_test]
791 async fn test_pattern() {
792 let srv = init_service(
793 App::new().service(web::resource(["/test", "/test2"]).to(async || HttpResponse::Ok())),
794 )
795 .await;
796 let req = TestRequest::with_uri("/test").to_request();
797 let resp = call_service(&srv, req).await;
798 assert_eq!(resp.status(), StatusCode::OK);
799 let req = TestRequest::with_uri("/test2").to_request();
800 let resp = call_service(&srv, req).await;
801 assert_eq!(resp.status(), StatusCode::OK);
802 }
803
804 #[crate::rt_test]
805 async fn test_default_resource() {
806 let srv = init_service(
807 App::new()
808 .service(web::resource("/test").route(web::get().to(async || HttpResponse::Ok())))
809 .default_service(async move |r: WebRequest<()>| {
810 Ok::<_, Infallible>(r.into_response(HttpResponse::BadRequest()))
811 }),
812 )
813 .await;
814 let req = TestRequest::with_uri("/test").to_request();
815 let resp = call_service(&srv, req).await;
816 assert_eq!(resp.status(), StatusCode::OK);
817
818 let req = TestRequest::with_uri("/test")
819 .method(Method::POST)
820 .to_request();
821 let resp = call_service(&srv, req).await;
822 assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
823
824 let srv = init_service(
825 App::new().service(
826 web::resource("/test")
827 .route(web::get().to(async || HttpResponse::Ok()))
828 .default_service(async move |r: WebRequest<()>| {
829 Ok::<_, Infallible>(r.into_response(HttpResponse::BadRequest()))
830 }),
831 ),
832 )
833 .await;
834
835 let req = TestRequest::with_uri("/test").to_request();
836 let resp = call_service(&srv, req).await;
837 assert_eq!(resp.status(), StatusCode::OK);
838
839 let req = TestRequest::with_uri("/test")
840 .method(Method::POST)
841 .to_request();
842 let resp = call_service(&srv, req).await;
843 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
844 }
845
846 #[crate::rt_test]
847 async fn test_resource_guards() {
848 let srv = init_service(
849 App::new()
850 .service(
851 web::resource("/test/{p}")
852 .guard(guard::Get())
853 .to(async || HttpResponse::Ok()),
854 )
855 .service(
856 web::resource("/test/{p}")
857 .guard(guard::Put())
858 .to(async || HttpResponse::Created()),
859 )
860 .service(
861 web::resource("/test/{p}")
862 .guard(guard::Delete())
863 .to(async || HttpResponse::NoContent()),
864 ),
865 )
866 .await;
867
868 let req = TestRequest::with_uri("/test/it")
869 .method(Method::GET)
870 .to_request();
871 let resp = call_service(&srv, req).await;
872 assert_eq!(resp.status(), StatusCode::OK);
873
874 let req = TestRequest::with_uri("/test/it")
875 .method(Method::PUT)
876 .to_request();
877 let resp = call_service(&srv, req).await;
878 assert_eq!(resp.status(), StatusCode::CREATED);
879
880 let req = TestRequest::with_uri("/test/it")
881 .method(Method::DELETE)
882 .to_request();
883 let resp = call_service(&srv, req).await;
884 assert_eq!(resp.status(), StatusCode::NO_CONTENT);
885 }
886
887 #[crate::rt_test]
888 async fn test_middleware() {
889 use crate::http::header::{CONTENT_TYPE, HeaderValue};
890 use crate::web::middleware::DefaultHeaders;
891
892 let srv = init_service(
893 App::new().service(
894 web::resource("/test")
895 .middleware(DefaultHeaders::new().header(CONTENT_TYPE, "text/plain"))
896 .route(web::get().to(async || HttpResponse::Ok())),
897 ),
898 )
899 .await;
900 for method in [Method::GET, Method::POST] {
901 let req = TestRequest::with_uri("/test").method(method).to_request();
902 let resp = call_service(&srv, req).await;
903 assert_eq!(
904 resp.headers().get(CONTENT_TYPE),
905 Some(&HeaderValue::from_static("text/plain"))
906 );
907 }
908 }
909
910 #[crate::rt_test]
911 async fn test_empty_path_in_scope() {
912 let srv = init_service(App::new().service(
913 web::scope("/app").service(web::resource("").to(async || HttpResponse::Ok())),
914 ))
915 .await;
916 let resp = call_service(&srv, TestRequest::with_uri("/app").to_request()).await;
917 assert_eq!(resp.status(), StatusCode::OK);
918 let resp = call_service(&srv, TestRequest::with_uri("/app/").to_request()).await;
919 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
920 }
921
922 #[crate::rt_test]
923 async fn test_into_factory() {
924 use crate::service::{IntoServiceFactory, ServiceFactory};
925
926 let res = web::resource("/test")
927 .route(web::get().to(async || HttpResponse::Ok()))
928 .default_service(async |r: WebRequest<()>| {
929 Ok::<_, Infallible>(r.into_response(HttpResponse::MethodNotAllowed()))
930 });
931 let srv = IntoServiceFactory::<_, (), WebRequest<()>>::into_factory(res)
932 .pipeline(())
933 .await
934 .unwrap();
935
936 let resp = srv
937 .call(TestRequest::default().to_srv_request())
938 .await
939 .unwrap();
940 assert_eq!(resp.status(), StatusCode::OK);
941 let req = TestRequest::default().method(Method::PUT).to_srv_request();
942 let resp = srv.call(req).await.unwrap();
943 assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
944 }
945}