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:
parent
4431c8da65
commit
c3ce33df05
150
src/app.rs
150
src/app.rs
@ -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 {
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! Commonly used middleware.
|
//! A collection of common middleware.
|
||||||
|
|
||||||
mod compat;
|
mod compat;
|
||||||
mod condition;
|
mod condition;
|
||||||
|
125
src/resource.rs
125
src/resource.rs
@ -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]
|
||||||
|
116
src/scope.rs
116
src/scope.rs
@ -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]
|
||||||
|
Loading…
Reference in New Issue
Block a user