1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-24 00:21:08 +01:00

unify generics across App, Scope and Resource (#2572)

Co-authored-by: Rob Ede <robjtede@icloud.com>
This commit is contained in:
Ali MJ Al-Nasrawy 2022-01-05 18:02:28 +03:00 committed by GitHub
parent 4431c8da65
commit c3ce33df05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 186 additions and 232 deletions

View File

@ -51,7 +51,10 @@ impl App<AppEntry> {
} }
} }
impl<T> App<T> { impl<T> App<T>
where
T: ServiceFactory<ServiceRequest, Config = (), Error = Error, InitError = ()>,
{
/// Set application (root level) data. /// Set application (root level) data.
/// ///
/// Application data stored with `App::app_data()` method is available through the /// Application data stored with `App::app_data()` method is available through the
@ -317,65 +320,63 @@ impl<T> App<T> {
self self
} }
/// Registers middleware, in the form of a middleware component (type), /// Registers an app-wide middleware.
/// that runs during inbound and/or outbound processing in the request
/// life-cycle (request -> response), modifying request/response as
/// necessary, across all requests managed by the *Application*.
/// ///
/// Use middleware when you need to read or modify *every* request or /// Registers middleware, in the form of a middleware compo nen t (type), that runs during
/// response in some way. /// inbound and/or outbound processing in the request life-cycle (request -> response),
/// modifying request/response as necessary, across all requests managed by the `App`.
/// ///
/// Notice that the keyword for registering middleware is `wrap`. As you /// Use middleware when you need to read or modify *every* request or response in some way.
/// register middleware using `wrap` in the App builder, imagine wrapping
/// layers around an inner App. The first middleware layer exposed to a
/// Request is the outermost layer-- the *last* registered in
/// the builder chain. Consequently, the *first* middleware registered
/// in the builder chain is the *last* to execute during request processing.
/// ///
/// Middleware can be applied similarly to individual `Scope`s and `Resource`s.
/// See [`Scope::wrap`](crate::Scope::wrap) and [`Resource::wrap`].
///
/// # Middleware Order
/// Notice that the keyword for registering middleware is `wrap`. As you register middleware
/// using `wrap` in the App builder, imagine wrapping layers around an inner App. The first
/// middleware layer exposed to a Request is the outermost layer (i.e., the *last* registered in
/// the builder chain). Consequently, the *first* middleware registered in the builder chain is
/// the *last* to start executing during request processing.
///
/// Ordering is less obvious when wrapped services also have middleware applied. In this case,
/// middlewares are run in reverse order for `App` _and then_ in reverse order for the
/// wrapped service.
///
/// # Examples
/// ``` /// ```
/// use actix_service::Service;
/// use actix_web::{middleware, web, App}; /// use actix_web::{middleware, web, App};
/// use actix_web::http::header::{CONTENT_TYPE, HeaderValue};
/// ///
/// async fn index() -> &'static str { /// async fn index() -> &'static str {
/// "Welcome!" /// "Welcome!"
/// } /// }
/// ///
/// fn main() { /// let app = App::new()
/// let app = App::new() /// .wrap(middleware::Logger::default())
/// .wrap(middleware::Logger::default()) /// .route("/index.html", web::get().to(index));
/// .route("/index.html", web::get().to(index));
/// }
/// ``` /// ```
pub fn wrap<M, B, B1>( #[doc(alias = "middleware")]
#[doc(alias = "use")] // nodejs terminology
pub fn wrap<M, B>(
self, self,
mw: M, mw: M,
) -> App< ) -> App<
impl ServiceFactory< impl ServiceFactory<
ServiceRequest, ServiceRequest,
Config = (), Config = (),
Response = ServiceResponse<B1>, Response = ServiceResponse<B>,
Error = Error, Error = Error,
InitError = (), InitError = (),
>, >,
> >
where where
T: ServiceFactory<
ServiceRequest,
Response = ServiceResponse<B>,
Error = Error,
Config = (),
InitError = (),
>,
B: MessageBody,
M: Transform< M: Transform<
T::Service, T::Service,
ServiceRequest, ServiceRequest,
Response = ServiceResponse<B1>, Response = ServiceResponse<B>,
Error = Error, Error = Error,
InitError = (), InitError = (),
>, > + 'static,
B1: MessageBody, B: MessageBody,
{ {
App { App {
endpoint: apply(mw, self.endpoint), endpoint: apply(mw, self.endpoint),
@ -388,61 +389,57 @@ impl<T> App<T> {
} }
} }
/// Registers middleware, in the form of a closure, that runs during inbound /// Registers an app-wide function middleware.
/// and/or outbound processing in the request life-cycle (request -> response), ///
/// modifying request/response as necessary, across all requests managed by /// `mw` is a closure that runs during inbound and/or outbound processing in the request
/// the *Application*. /// life-cycle (request -> response), modifying request/response as necessary, across all
/// requests handled by the `App`.
/// ///
/// Use middleware when you need to read or modify *every* request or response in some way. /// Use middleware when you need to read or modify *every* request or response in some way.
/// ///
/// Middleware can also be applied to individual `Scope`s and `Resource`s.
///
/// See [`App::wrap`] for details on how middlewares compose with each other.
///
/// # Examples
/// ``` /// ```
/// use actix_service::Service; /// use actix_web::{dev::Service as _, middleware, web, App};
/// use actix_web::{web, App};
/// use actix_web::http::header::{CONTENT_TYPE, HeaderValue}; /// use actix_web::http::header::{CONTENT_TYPE, HeaderValue};
/// ///
/// async fn index() -> &'static str { /// async fn index() -> &'static str {
/// "Welcome!" /// "Welcome!"
/// } /// }
/// ///
/// fn main() { /// let app = App::new()
/// let app = App::new() /// .wrap_fn(|req, srv| {
/// .wrap_fn(|req, srv| { /// let fut = srv.call(req);
/// let fut = srv.call(req); /// async {
/// async { /// let mut res = fut.await?;
/// let mut res = fut.await?; /// res.headers_mut()
/// res.headers_mut().insert( /// .insert(CONTENT_TYPE, HeaderValue::from_static("text/plain"));
/// CONTENT_TYPE, HeaderValue::from_static("text/plain"), /// Ok(res)
/// ); /// }
/// Ok(res) /// })
/// } /// .route("/index.html", web::get().to(index));
/// })
/// .route("/index.html", web::get().to(index));
/// }
/// ``` /// ```
pub fn wrap_fn<F, R, B, B1>( #[doc(alias = "middleware")]
#[doc(alias = "use")] // nodejs terminology
pub fn wrap_fn<F, R, B>(
self, self,
mw: F, mw: F,
) -> App< ) -> App<
impl ServiceFactory< impl ServiceFactory<
ServiceRequest, ServiceRequest,
Config = (), Config = (),
Response = ServiceResponse<B1>, Response = ServiceResponse<B>,
Error = Error, Error = Error,
InitError = (), InitError = (),
>, >,
> >
where where
T: ServiceFactory< F: Fn(ServiceRequest, &T::Service) -> R + Clone + 'static,
ServiceRequest, R: Future<Output = Result<ServiceResponse<B>, Error>>,
Response = ServiceResponse<B>,
Error = Error,
Config = (),
InitError = (),
>,
B: MessageBody, B: MessageBody,
F: Fn(ServiceRequest, &T::Service) -> R + Clone,
R: Future<Output = Result<ServiceResponse<B1>, Error>>,
B1: MessageBody,
{ {
App { App {
endpoint: apply_fn_factory(self.endpoint, mw), endpoint: apply_fn_factory(self.endpoint, mw),
@ -458,15 +455,14 @@ impl<T> App<T> {
impl<T, B> IntoServiceFactory<AppInit<T, B>, Request> for App<T> impl<T, B> IntoServiceFactory<AppInit<T, B>, Request> for App<T>
where where
B: MessageBody,
T: ServiceFactory< T: ServiceFactory<
ServiceRequest, ServiceRequest,
Config = (), Config = (),
Response = ServiceResponse<B>, Response = ServiceResponse<B>,
Error = Error, Error = Error,
InitError = (), InitError = (),
>, > + 'static,
T::Future: 'static, B: MessageBody,
{ {
fn into_factory(self) -> AppInit<T, B> { fn into_factory(self) -> AppInit<T, B> {
AppInit { AppInit {

View File

@ -150,11 +150,13 @@ mod tests {
use actix_service::IntoService; use actix_service::IntoService;
use crate::dev::ServiceRequest; use crate::{
use crate::http::StatusCode; dev::ServiceRequest,
use crate::middleware::{self, Condition, Logger}; http::StatusCode,
use crate::test::{call_service, init_service, TestRequest}; middleware::{self, Condition, Logger},
use crate::{web, App, HttpResponse}; test::{self, call_service, init_service, TestRequest},
web, App, HttpResponse,
};
#[actix_rt::test] #[actix_rt::test]
#[cfg(all(feature = "cookies", feature = "__compress"))] #[cfg(all(feature = "cookies", feature = "__compress"))]
@ -219,4 +221,17 @@ mod tests {
let resp = call_service(&mw, TestRequest::default().to_srv_request()).await; let resp = call_service(&mw, TestRequest::default().to_srv_request()).await;
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
} }
#[actix_rt::test]
async fn compat_noop_is_noop() {
let srv = test::ok_service();
let mw = Compat::noop()
.new_transform(srv.into_service())
.await
.unwrap();
let resp = call_service(&mw, TestRequest::default().to_srv_request()).await;
assert_eq!(resp.status(), StatusCode::OK);
}
} }

View File

@ -1,4 +1,4 @@
//! Commonly used middleware. //! A collection of common middleware.
mod compat; mod compat;
mod condition; mod condition;

View File

@ -1,6 +1,6 @@
use std::{cell::RefCell, fmt, future::Future, marker::PhantomData, rc::Rc}; use std::{cell::RefCell, fmt, future::Future, rc::Rc};
use actix_http::{body::BoxBody, Extensions}; use actix_http::Extensions;
use actix_router::{IntoPatterns, Patterns}; use actix_router::{IntoPatterns, Patterns};
use actix_service::{ use actix_service::{
apply, apply_fn_factory, boxed, fn_service, IntoServiceFactory, Service, ServiceFactory, apply, apply_fn_factory, boxed, fn_service, IntoServiceFactory, Service, ServiceFactory,
@ -42,7 +42,7 @@ use crate::{
/// ///
/// If no matching route could be found, *405* response code get returned. Default behavior could be /// If no matching route could be found, *405* response code get returned. Default behavior could be
/// overridden with `default_resource()` method. /// overridden with `default_resource()` method.
pub struct Resource<T = ResourceEndpoint, B = BoxBody> { pub struct Resource<T = ResourceEndpoint> {
endpoint: T, endpoint: T,
rdef: Patterns, rdef: Patterns,
name: Option<String>, name: Option<String>,
@ -51,7 +51,6 @@ pub struct Resource<T = ResourceEndpoint, B = BoxBody> {
guards: Vec<Box<dyn Guard>>, guards: Vec<Box<dyn Guard>>,
default: BoxedHttpServiceFactory, default: BoxedHttpServiceFactory,
factory_ref: Rc<RefCell<Option<ResourceFactory>>>, factory_ref: Rc<RefCell<Option<ResourceFactory>>>,
_phantom: PhantomData<B>,
} }
impl Resource { impl Resource {
@ -69,21 +68,13 @@ impl Resource {
default: boxed::factory(fn_service(|req: ServiceRequest| async { default: boxed::factory(fn_service(|req: ServiceRequest| async {
Ok(req.into_response(HttpResponse::MethodNotAllowed())) Ok(req.into_response(HttpResponse::MethodNotAllowed()))
})), })),
_phantom: PhantomData,
} }
} }
} }
impl<T, B> Resource<T, B> impl<T> Resource<T>
where where
T: ServiceFactory< T: ServiceFactory<ServiceRequest, Config = (), Error = Error, InitError = ()>,
ServiceRequest,
Config = (),
Response = ServiceResponse<B>,
Error = Error,
InitError = (),
>,
B: MessageBody,
{ {
/// Set resource name. /// Set resource name.
/// ///
@ -241,35 +232,35 @@ where
self self
} }
/// Register a resource middleware. /// Registers a resource middleware.
/// ///
/// This is similar to `App's` middlewares, but middleware get invoked on resource level. /// `mw` is a middleware component (type), that can modify the request and response across all
/// Resource level middlewares are not allowed to change response /// routes managed by this `Resource`.
/// type (i.e modify response's body).
/// ///
/// **Note**: middlewares get called in opposite order of middlewares registration. /// See [`App::wrap`](crate::App::wrap) for more details.
pub fn wrap<M, B1>( #[doc(alias = "middleware")]
#[doc(alias = "use")] // nodejs terminology
pub fn wrap<M, B>(
self, self,
mw: M, mw: M,
) -> Resource< ) -> Resource<
impl ServiceFactory< impl ServiceFactory<
ServiceRequest, ServiceRequest,
Config = (), Config = (),
Response = ServiceResponse<B1>, Response = ServiceResponse<B>,
Error = Error, Error = Error,
InitError = (), InitError = (),
>, >,
B1,
> >
where where
M: Transform< M: Transform<
T::Service, T::Service,
ServiceRequest, ServiceRequest,
Response = ServiceResponse<B1>, Response = ServiceResponse<B>,
Error = Error, Error = Error,
InitError = (), InitError = (),
>, > + 'static,
B1: MessageBody, B: MessageBody,
{ {
Resource { Resource {
endpoint: apply(mw, self.endpoint), endpoint: apply(mw, self.endpoint),
@ -280,61 +271,34 @@ where
default: self.default, default: self.default,
app_data: self.app_data, app_data: self.app_data,
factory_ref: self.factory_ref, factory_ref: self.factory_ref,
_phantom: PhantomData,
} }
} }
/// Register a resource middleware function. /// Registers a resource function middleware.
/// ///
/// This function accepts instance of `ServiceRequest` type and /// `mw` is a closure that runs during inbound and/or outbound processing in the request
/// mutable reference to the next middleware in chain. /// life-cycle (request -> response), modifying request/response as necessary, across all
/// requests handled by the `Resource`.
/// ///
/// This is similar to `App's` middlewares, but middleware get invoked on resource level. /// See [`App::wrap_fn`](crate::App::wrap_fn) for examples and more details.
/// Resource level middlewares are not allowed to change response #[doc(alias = "middleware")]
/// type (i.e modify response's body). #[doc(alias = "use")] // nodejs terminology
/// pub fn wrap_fn<F, R, B>(
/// ```
/// use actix_service::Service;
/// use actix_web::{web, App};
/// use actix_web::http::header::{CONTENT_TYPE, HeaderValue};
///
/// async fn index() -> &'static str {
/// "Welcome!"
/// }
///
/// fn main() {
/// let app = App::new().service(
/// web::resource("/index.html")
/// .wrap_fn(|req, srv| {
/// let fut = srv.call(req);
/// async {
/// let mut res = fut.await?;
/// res.headers_mut().insert(
/// CONTENT_TYPE, HeaderValue::from_static("text/plain"),
/// );
/// Ok(res)
/// }
/// })
/// .route(web::get().to(index)));
/// }
/// ```
pub fn wrap_fn<F, R, B1>(
self, self,
mw: F, mw: F,
) -> Resource< ) -> Resource<
impl ServiceFactory< impl ServiceFactory<
ServiceRequest, ServiceRequest,
Config = (), Config = (),
Response = ServiceResponse<B1>, Response = ServiceResponse<B>,
Error = Error, Error = Error,
InitError = (), InitError = (),
>, >,
B1,
> >
where where
F: Fn(ServiceRequest, &T::Service) -> R + Clone, F: Fn(ServiceRequest, &T::Service) -> R + Clone + 'static,
R: Future<Output = Result<ServiceResponse<B1>, Error>>, R: Future<Output = Result<ServiceResponse<B>, Error>>,
B1: MessageBody, B: MessageBody,
{ {
Resource { Resource {
endpoint: apply_fn_factory(self.endpoint, mw), endpoint: apply_fn_factory(self.endpoint, mw),
@ -345,7 +309,6 @@ where
default: self.default, default: self.default,
app_data: self.app_data, app_data: self.app_data,
factory_ref: self.factory_ref, factory_ref: self.factory_ref,
_phantom: PhantomData,
} }
} }
@ -373,7 +336,7 @@ where
} }
} }
impl<T, B> HttpServiceFactory for Resource<T, B> impl<T, B> HttpServiceFactory for Resource<T>
where where
T: ServiceFactory< T: ServiceFactory<
ServiceRequest, ServiceRequest,
@ -517,7 +480,7 @@ mod tests {
header::{self, HeaderValue}, header::{self, HeaderValue},
Method, StatusCode, Method, StatusCode,
}, },
middleware::{Compat, DefaultHeaders}, middleware::DefaultHeaders,
service::{ServiceRequest, ServiceResponse}, service::{ServiceRequest, ServiceResponse},
test::{call_service, init_service, TestRequest}, test::{call_service, init_service, TestRequest},
web, App, Error, HttpMessage, HttpResponse, web, App, Error, HttpMessage, HttpResponse,
@ -525,31 +488,35 @@ mod tests {
#[test] #[test]
fn can_be_returned_from_fn() { fn can_be_returned_from_fn() {
fn my_resource() -> Resource { fn my_resource_1() -> Resource {
web::resource("/test").route(web::get().to(|| async { "hello" })) web::resource("/test1").route(web::get().to(|| async { "hello" }))
} }
fn my_compat_resource() -> Resource< fn my_resource_2() -> Resource<
impl ServiceFactory< impl ServiceFactory<
ServiceRequest, ServiceRequest,
Config = (), Config = (),
Response = ServiceResponse, Response = ServiceResponse<impl MessageBody>,
Error = Error, Error = Error,
InitError = (), InitError = (),
>, >,
> { > {
web::resource("/test-compat") web::resource("/test2")
.wrap_fn(|req, srv| { .wrap_fn(|req, srv| {
let fut = srv.call(req); let fut = srv.call(req);
async { Ok(fut.await?.map_into_right_body::<()>()) } async { Ok(fut.await?.map_into_right_body::<()>()) }
}) })
.wrap(Compat::noop())
.route(web::get().to(|| async { "hello" })) .route(web::get().to(|| async { "hello" }))
} }
fn my_resource_3() -> impl HttpServiceFactory {
web::resource("/test2").route(web::get().to(|| async { "hello" }))
}
App::new() App::new()
.service(my_resource()) .service(my_resource_1())
.service(my_compat_resource()); .service(my_resource_2())
.service(my_resource_3());
} }
#[actix_rt::test] #[actix_rt::test]

View File

@ -1,9 +1,6 @@
use std::{cell::RefCell, fmt, future::Future, marker::PhantomData, mem, rc::Rc}; use std::{cell::RefCell, fmt, future::Future, mem, rc::Rc};
use actix_http::{ use actix_http::{body::MessageBody, Extensions};
body::{BoxBody, MessageBody},
Extensions,
};
use actix_router::{ResourceDef, Router}; use actix_router::{ResourceDef, Router};
use actix_service::{ use actix_service::{
apply, apply_fn_factory, boxed, IntoServiceFactory, Service, ServiceFactory, apply, apply_fn_factory, boxed, IntoServiceFactory, Service, ServiceFactory,
@ -57,7 +54,7 @@ type Guards = Vec<Box<dyn Guard>>;
/// ///
/// [pat]: crate::dev::ResourceDef#prefix-resources /// [pat]: crate::dev::ResourceDef#prefix-resources
/// [dynamic segments]: crate::dev::ResourceDef#dynamic-segments /// [dynamic segments]: crate::dev::ResourceDef#dynamic-segments
pub struct Scope<T = ScopeEndpoint, B = BoxBody> { pub struct Scope<T = ScopeEndpoint> {
endpoint: T, endpoint: T,
rdef: String, rdef: String,
app_data: Option<Extensions>, app_data: Option<Extensions>,
@ -66,7 +63,6 @@ pub struct Scope<T = ScopeEndpoint, B = BoxBody> {
default: Option<Rc<BoxedHttpServiceFactory>>, default: Option<Rc<BoxedHttpServiceFactory>>,
external: Vec<ResourceDef>, external: Vec<ResourceDef>,
factory_ref: Rc<RefCell<Option<ScopeFactory>>>, factory_ref: Rc<RefCell<Option<ScopeFactory>>>,
_phantom: PhantomData<B>,
} }
impl Scope { impl Scope {
@ -83,21 +79,13 @@ impl Scope {
default: None, default: None,
external: Vec::new(), external: Vec::new(),
factory_ref, factory_ref,
_phantom: Default::default(),
} }
} }
} }
impl<T, B> Scope<T, B> impl<T> Scope<T>
where where
T: ServiceFactory< T: ServiceFactory<ServiceRequest, Config = (), Error = Error, InitError = ()>,
ServiceRequest,
Config = (),
Response = ServiceResponse<B>,
Error = Error,
InitError = (),
>,
B: 'static,
{ {
/// Add match guard to a scope. /// Add match guard to a scope.
/// ///
@ -296,32 +284,35 @@ where
self self
} }
/// Registers middleware, in the form of a middleware component (type), that runs during inbound /// Registers a scope-wide middleware.
/// processing in the request life-cycle (request -> response), modifying request as necessary,
/// across all requests managed by the *Scope*.
/// ///
/// Use middleware when you need to read or modify *every* request in some way. /// `mw` is a middleware component (type), that can modify the request and response across all
pub fn wrap<M, B1>( /// sub-resources managed by this `Scope`.
///
/// See [`App::wrap`](crate::App::wrap) for more details.
#[doc(alias = "middleware")]
#[doc(alias = "use")] // nodejs terminology
pub fn wrap<M, B>(
self, self,
mw: M, mw: M,
) -> Scope< ) -> Scope<
impl ServiceFactory< impl ServiceFactory<
ServiceRequest, ServiceRequest,
Config = (), Config = (),
Response = ServiceResponse<B1>, Response = ServiceResponse<B>,
Error = Error, Error = Error,
InitError = (), InitError = (),
>, >,
B1,
> >
where where
M: Transform< M: Transform<
T::Service, T::Service,
ServiceRequest, ServiceRequest,
Response = ServiceResponse<B1>, Response = ServiceResponse<B>,
Error = Error, Error = Error,
InitError = (), InitError = (),
>, > + 'static,
B: MessageBody,
{ {
Scope { Scope {
endpoint: apply(mw, self.endpoint), endpoint: apply(mw, self.endpoint),
@ -332,54 +323,34 @@ where
default: self.default, default: self.default,
external: self.external, external: self.external,
factory_ref: self.factory_ref, factory_ref: self.factory_ref,
_phantom: PhantomData,
} }
} }
/// Registers middleware, in the form of a closure, that runs during inbound processing in the /// Registers a scope-wide function middleware.
/// request life-cycle (request -> response), modifying request as necessary, across all
/// requests managed by the *Scope*.
/// ///
/// # Examples /// `mw` is a closure that runs during inbound and/or outbound processing in the request
/// ``` /// life-cycle (request -> response), modifying request/response as necessary, across all
/// use actix_service::Service; /// requests handled by the `Scope`.
/// use actix_web::{web, App};
/// use actix_web::http::header::{CONTENT_TYPE, HeaderValue};
/// ///
/// async fn index() -> &'static str { /// See [`App::wrap_fn`](crate::App::wrap_fn) for examples and more details.
/// "Welcome!" #[doc(alias = "middleware")]
/// } #[doc(alias = "use")] // nodejs terminology
/// pub fn wrap_fn<F, R, B>(
/// let app = App::new().service(
/// web::scope("/app")
/// .wrap_fn(|req, srv| {
/// let fut = srv.call(req);
/// async {
/// let mut res = fut.await?;
/// res.headers_mut().insert(
/// CONTENT_TYPE, HeaderValue::from_static("text/plain"),
/// );
/// Ok(res)
/// }
/// })
/// .route("/index.html", web::get().to(index)));
/// ```
pub fn wrap_fn<F, R, B1>(
self, self,
mw: F, mw: F,
) -> Scope< ) -> Scope<
impl ServiceFactory< impl ServiceFactory<
ServiceRequest, ServiceRequest,
Config = (), Config = (),
Response = ServiceResponse<B1>, Response = ServiceResponse<B>,
Error = Error, Error = Error,
InitError = (), InitError = (),
>, >,
B1,
> >
where where
F: Fn(ServiceRequest, &T::Service) -> R + Clone, F: Fn(ServiceRequest, &T::Service) -> R + Clone + 'static,
R: Future<Output = Result<ServiceResponse<B1>, Error>>, R: Future<Output = Result<ServiceResponse<B>, Error>>,
B: MessageBody,
{ {
Scope { Scope {
endpoint: apply_fn_factory(self.endpoint, mw), endpoint: apply_fn_factory(self.endpoint, mw),
@ -390,12 +361,11 @@ where
default: self.default, default: self.default,
external: self.external, external: self.external,
factory_ref: self.factory_ref, factory_ref: self.factory_ref,
_phantom: PhantomData,
} }
} }
} }
impl<T, B> HttpServiceFactory for Scope<T, B> impl<T, B> HttpServiceFactory for Scope<T>
where where
T: ServiceFactory< T: ServiceFactory<
ServiceRequest, ServiceRequest,
@ -596,7 +566,7 @@ mod tests {
header::{self, HeaderValue}, header::{self, HeaderValue},
Method, StatusCode, Method, StatusCode,
}, },
middleware::{Compat, DefaultHeaders}, middleware::DefaultHeaders,
service::{ServiceRequest, ServiceResponse}, service::{ServiceRequest, ServiceResponse},
test::{assert_body_eq, call_service, init_service, read_body, TestRequest}, test::{assert_body_eq, call_service, init_service, read_body, TestRequest},
web, App, HttpMessage, HttpRequest, HttpResponse, web, App, HttpMessage, HttpRequest, HttpResponse,
@ -604,16 +574,16 @@ mod tests {
#[test] #[test]
fn can_be_returned_from_fn() { fn can_be_returned_from_fn() {
fn my_scope() -> Scope { fn my_scope_1() -> Scope {
web::scope("/test") web::scope("/test")
.service(web::resource("").route(web::get().to(|| async { "hello" }))) .service(web::resource("").route(web::get().to(|| async { "hello" })))
} }
fn my_compat_scope() -> Scope< fn my_scope_2() -> Scope<
impl ServiceFactory< impl ServiceFactory<
ServiceRequest, ServiceRequest,
Config = (), Config = (),
Response = ServiceResponse, Response = ServiceResponse<impl MessageBody>,
Error = Error, Error = Error,
InitError = (), InitError = (),
>, >,
@ -623,11 +593,17 @@ mod tests {
let fut = srv.call(req); let fut = srv.call(req);
async { Ok(fut.await?.map_into_right_body::<()>()) } async { Ok(fut.await?.map_into_right_body::<()>()) }
}) })
.wrap(Compat::noop())
.service(web::resource("").route(web::get().to(|| async { "hello" }))) .service(web::resource("").route(web::get().to(|| async { "hello" })))
} }
App::new().service(my_scope()).service(my_compat_scope()); fn my_scope_3() -> impl HttpServiceFactory {
my_scope_2()
}
App::new()
.service(my_scope_1())
.service(my_scope_2())
.service(my_scope_3());
} }
#[actix_rt::test] #[actix_rt::test]