mirror of
https://github.com/fafhrd91/actix-web
synced 2024-11-30 10:42:55 +01:00
allow any body type in Resource (#2526)
This commit is contained in:
parent
1769812d0b
commit
cd025f5c0b
@ -1,6 +1,10 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
### Changed
|
||||||
|
- No longer require `Resource` service body type to be boxed. [#2526]
|
||||||
|
|
||||||
|
[#2526]: https://github.com/actix/actix-web/pull/2526
|
||||||
|
|
||||||
|
|
||||||
## 4.0.0-beta.15 - 2021-12-17
|
## 4.0.0-beta.15 - 2021-12-17
|
||||||
|
@ -55,7 +55,6 @@ bytestring = "1"
|
|||||||
derive_more = "0.99.5"
|
derive_more = "0.99.5"
|
||||||
encoding_rs = "0.8"
|
encoding_rs = "0.8"
|
||||||
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
||||||
futures-task = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
|
||||||
h2 = "0.3.9"
|
h2 = "0.3.9"
|
||||||
http = "0.2.5"
|
http = "0.2.5"
|
||||||
httparse = "1.5.1"
|
httparse = "1.5.1"
|
||||||
|
@ -38,6 +38,15 @@ pub struct Compat<T> {
|
|||||||
transform: T,
|
transform: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl Compat<super::Noop> {
|
||||||
|
pub(crate) fn noop() -> Self {
|
||||||
|
Self {
|
||||||
|
transform: super::Noop,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> Compat<T> {
|
impl<T> Compat<T> {
|
||||||
/// Wrap a middleware to give it broader compatibility.
|
/// Wrap a middleware to give it broader compatibility.
|
||||||
pub fn new(middleware: T) -> Self {
|
pub fn new(middleware: T) -> Self {
|
||||||
|
@ -5,6 +5,8 @@ mod condition;
|
|||||||
mod default_headers;
|
mod default_headers;
|
||||||
mod err_handlers;
|
mod err_handlers;
|
||||||
mod logger;
|
mod logger;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod noop;
|
||||||
mod normalize;
|
mod normalize;
|
||||||
|
|
||||||
pub use self::compat::Compat;
|
pub use self::compat::Compat;
|
||||||
@ -12,6 +14,8 @@ pub use self::condition::Condition;
|
|||||||
pub use self::default_headers::DefaultHeaders;
|
pub use self::default_headers::DefaultHeaders;
|
||||||
pub use self::err_handlers::{ErrorHandlerResponse, ErrorHandlers};
|
pub use self::err_handlers::{ErrorHandlerResponse, ErrorHandlers};
|
||||||
pub use self::logger::Logger;
|
pub use self::logger::Logger;
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) use self::noop::Noop;
|
||||||
pub use self::normalize::{NormalizePath, TrailingSlash};
|
pub use self::normalize::{NormalizePath, TrailingSlash};
|
||||||
|
|
||||||
#[cfg(feature = "__compress")]
|
#[cfg(feature = "__compress")]
|
||||||
|
37
src/middleware/noop.rs
Normal file
37
src/middleware/noop.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
//! A no-op middleware. See [Noop] for docs.
|
||||||
|
|
||||||
|
use actix_utils::future::{ready, Ready};
|
||||||
|
|
||||||
|
use crate::dev::{Service, Transform};
|
||||||
|
|
||||||
|
/// A no-op middleware that passes through request and response untouched.
|
||||||
|
pub(crate) struct Noop;
|
||||||
|
|
||||||
|
impl<S: Service<Req>, Req> Transform<S, Req> for Noop {
|
||||||
|
type Response = S::Response;
|
||||||
|
type Error = S::Error;
|
||||||
|
type Transform = NoopService<S>;
|
||||||
|
type InitError = ();
|
||||||
|
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
||||||
|
|
||||||
|
fn new_transform(&self, service: S) -> Self::Future {
|
||||||
|
ready(Ok(NoopService { service }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub(crate) struct NoopService<S> {
|
||||||
|
service: S,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Service<Req>, Req> Service<Req> for NoopService<S> {
|
||||||
|
type Response = S::Response;
|
||||||
|
type Error = S::Error;
|
||||||
|
type Future = S::Future;
|
||||||
|
|
||||||
|
crate::dev::forward_ready!(service);
|
||||||
|
|
||||||
|
fn call(&self, req: Req) -> Self::Future {
|
||||||
|
self.service.call(req)
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
use std::{cell::RefCell, fmt, future::Future, rc::Rc};
|
use std::{cell::RefCell, fmt, future::Future, marker::PhantomData, rc::Rc};
|
||||||
|
|
||||||
use actix_http::Extensions;
|
use actix_http::{body::BoxBody, 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,
|
||||||
@ -45,7 +45,7 @@ use crate::{
|
|||||||
///
|
///
|
||||||
/// If no matching route could be found, *405* response code get returned.
|
/// If no matching route could be found, *405* response code get returned.
|
||||||
/// Default behavior could be overridden with `default_resource()` method.
|
/// Default behavior could be overridden with `default_resource()` method.
|
||||||
pub struct Resource<T = ResourceEndpoint> {
|
pub struct Resource<T = ResourceEndpoint, B = BoxBody> {
|
||||||
endpoint: T,
|
endpoint: T,
|
||||||
rdef: Patterns,
|
rdef: Patterns,
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
@ -54,6 +54,7 @@ pub struct Resource<T = ResourceEndpoint> {
|
|||||||
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 {
|
||||||
@ -71,19 +72,21 @@ 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> Resource<T>
|
impl<T, B> Resource<T, B>
|
||||||
where
|
where
|
||||||
T: ServiceFactory<
|
T: ServiceFactory<
|
||||||
ServiceRequest,
|
ServiceRequest,
|
||||||
Config = (),
|
Config = (),
|
||||||
Response = ServiceResponse,
|
Response = ServiceResponse<B>,
|
||||||
Error = Error,
|
Error = Error,
|
||||||
InitError = (),
|
InitError = (),
|
||||||
>,
|
>,
|
||||||
|
B: MessageBody,
|
||||||
{
|
{
|
||||||
/// Set resource name.
|
/// Set resource name.
|
||||||
///
|
///
|
||||||
@ -252,26 +255,28 @@ where
|
|||||||
/// type (i.e modify response's body).
|
/// type (i.e modify response's body).
|
||||||
///
|
///
|
||||||
/// **Note**: middlewares get called in opposite order of middlewares registration.
|
/// **Note**: middlewares get called in opposite order of middlewares registration.
|
||||||
pub fn wrap<M>(
|
pub fn wrap<M, B1>(
|
||||||
self,
|
self,
|
||||||
mw: M,
|
mw: M,
|
||||||
) -> Resource<
|
) -> Resource<
|
||||||
impl ServiceFactory<
|
impl ServiceFactory<
|
||||||
ServiceRequest,
|
ServiceRequest,
|
||||||
Config = (),
|
Config = (),
|
||||||
Response = ServiceResponse,
|
Response = ServiceResponse<B1>,
|
||||||
Error = Error,
|
Error = Error,
|
||||||
InitError = (),
|
InitError = (),
|
||||||
>,
|
>,
|
||||||
|
B1,
|
||||||
>
|
>
|
||||||
where
|
where
|
||||||
M: Transform<
|
M: Transform<
|
||||||
T::Service,
|
T::Service,
|
||||||
ServiceRequest,
|
ServiceRequest,
|
||||||
Response = ServiceResponse,
|
Response = ServiceResponse<B1>,
|
||||||
Error = Error,
|
Error = Error,
|
||||||
InitError = (),
|
InitError = (),
|
||||||
>,
|
>,
|
||||||
|
B1: MessageBody,
|
||||||
{
|
{
|
||||||
Resource {
|
Resource {
|
||||||
endpoint: apply(mw, self.endpoint),
|
endpoint: apply(mw, self.endpoint),
|
||||||
@ -282,6 +287,7 @@ 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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,21 +325,23 @@ where
|
|||||||
/// .route(web::get().to(index)));
|
/// .route(web::get().to(index)));
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn wrap_fn<F, R>(
|
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,
|
Response = ServiceResponse<B1>,
|
||||||
Error = Error,
|
Error = Error,
|
||||||
InitError = (),
|
InitError = (),
|
||||||
>,
|
>,
|
||||||
|
B1,
|
||||||
>
|
>
|
||||||
where
|
where
|
||||||
F: Fn(ServiceRequest, &T::Service) -> R + Clone,
|
F: Fn(ServiceRequest, &T::Service) -> R + Clone,
|
||||||
R: Future<Output = Result<ServiceResponse, Error>>,
|
R: Future<Output = Result<ServiceResponse<B1>, Error>>,
|
||||||
|
B1: MessageBody,
|
||||||
{
|
{
|
||||||
Resource {
|
Resource {
|
||||||
endpoint: apply_fn_factory(self.endpoint, mw),
|
endpoint: apply_fn_factory(self.endpoint, mw),
|
||||||
@ -344,6 +352,7 @@ 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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,15 +380,16 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> HttpServiceFactory for Resource<T>
|
impl<T, B> HttpServiceFactory for Resource<T, B>
|
||||||
where
|
where
|
||||||
T: ServiceFactory<
|
T: ServiceFactory<
|
||||||
ServiceRequest,
|
ServiceRequest,
|
||||||
Config = (),
|
Config = (),
|
||||||
Response = ServiceResponse,
|
Response = ServiceResponse<B>,
|
||||||
Error = Error,
|
Error = Error,
|
||||||
InitError = (),
|
InitError = (),
|
||||||
> + 'static,
|
> + 'static,
|
||||||
|
B: MessageBody + 'static,
|
||||||
{
|
{
|
||||||
fn register(mut self, config: &mut AppService) {
|
fn register(mut self, config: &mut AppService) {
|
||||||
let guards = if self.guards.is_empty() {
|
let guards = if self.guards.is_empty() {
|
||||||
@ -411,7 +421,9 @@ where
|
|||||||
req.add_data_container(Rc::clone(data));
|
req.add_data_container(Rc::clone(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
srv.call(req)
|
let fut = srv.call(req);
|
||||||
|
|
||||||
|
async { Ok(fut.await?.map_into_boxed_body()) }
|
||||||
});
|
});
|
||||||
|
|
||||||
config.register_service(rdef, guards, endpoint, None)
|
config.register_service(rdef, guards, endpoint, None)
|
||||||
@ -534,11 +546,11 @@ mod tests {
|
|||||||
>,
|
>,
|
||||||
> {
|
> {
|
||||||
web::resource("/test-compat")
|
web::resource("/test-compat")
|
||||||
// .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::new(DefaultHeaders::new()))
|
.wrap(Compat::noop())
|
||||||
.route(web::get().to(|| async { "hello" }))
|
.route(web::get().to(|| async { "hello" }))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -801,4 +813,26 @@ mod tests {
|
|||||||
let resp = call_service(&srv, req).await;
|
let resp = call_service(&srv, req).await;
|
||||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_middleware_body_type() {
|
||||||
|
let srv = init_service(
|
||||||
|
App::new().service(
|
||||||
|
web::resource("/test")
|
||||||
|
.wrap_fn(|req, srv| {
|
||||||
|
let fut = srv.call(req);
|
||||||
|
async { Ok(fut.await?.map_into_right_body::<()>()) }
|
||||||
|
})
|
||||||
|
.route(web::get().to(|| async { "hello" })),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// test if `try_into_bytes()` is preserved across scope layer
|
||||||
|
use actix_http::body::MessageBody as _;
|
||||||
|
let req = TestRequest::with_uri("/test").to_request();
|
||||||
|
let resp = call_service(&srv, req).await;
|
||||||
|
let body = resp.into_body();
|
||||||
|
assert_eq!(body.try_into_bytes().unwrap(), b"hello".as_ref());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
10
src/scope.rs
10
src/scope.rs
@ -616,11 +616,11 @@ mod tests {
|
|||||||
>,
|
>,
|
||||||
> {
|
> {
|
||||||
web::scope("/test-compat")
|
web::scope("/test-compat")
|
||||||
// .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::new(DefaultHeaders::new()))
|
.wrap(Compat::noop())
|
||||||
.service(web::resource("").route(web::get().to(|| async { "hello" })))
|
.service(web::resource("").route(web::get().to(|| async { "hello" })))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user