mirror of
https://github.com/ntex-rs/ntex.git
synced 2025-04-03 21:07:39 +03:00
Allow to use generic middlewares #390
This commit is contained in:
parent
5f20ee2be5
commit
ee640ab335
10 changed files with 112 additions and 27 deletions
|
@ -1,5 +1,9 @@
|
|||
# Changes
|
||||
|
||||
## [2.3.0] - 2024-08-13
|
||||
|
||||
* web: Allow to use generic middlewares #390
|
||||
|
||||
## [2.2.0] - 2024-08-12
|
||||
|
||||
* Http server gracefull shutdown support
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "ntex"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
authors = ["ntex contributors <team@ntex.rs>"]
|
||||
description = "Framework for composable network services"
|
||||
readme = "README.md"
|
||||
|
|
|
@ -28,6 +28,7 @@ async fn main() -> std::io::Result<()> {
|
|||
.service((index, no_params))
|
||||
.service(
|
||||
web::resource("/resource2/index.html")
|
||||
.wrap(ntex::util::timeout::Timeout::new(ntex::time::Millis(5000)))
|
||||
.wrap(middleware::DefaultHeaders::new().header("X-Version-R2", "0.3"))
|
||||
.default_service(
|
||||
web::route().to(|| async { HttpResponse::MethodNotAllowed() }),
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::service::boxed::{self, BoxServiceFactory};
|
|||
use crate::service::{
|
||||
chain_factory, dev::ServiceChainFactory, map_config, IntoServiceFactory,
|
||||
};
|
||||
use crate::service::{Identity, Middleware, Service, ServiceCtx, ServiceFactory, Stack};
|
||||
use crate::service::{Identity, Middleware, Service, ServiceCtx, ServiceFactory};
|
||||
use crate::util::{BoxFuture, Extensions};
|
||||
|
||||
use super::app_service::{AppFactory, AppService};
|
||||
|
@ -16,6 +16,7 @@ use super::resource::Resource;
|
|||
use super::response::WebResponse;
|
||||
use super::route::Route;
|
||||
use super::service::{AppServiceFactory, ServiceFactoryWrapper, WebServiceFactory};
|
||||
use super::stack::WebStack;
|
||||
use super::{DefaultError, ErrorRenderer};
|
||||
|
||||
type HttpNewService<Err: ErrorRenderer> =
|
||||
|
@ -396,9 +397,9 @@ where
|
|||
/// .route("/index.html", web::get().to(index));
|
||||
/// }
|
||||
/// ```
|
||||
pub fn wrap<U>(self, mw: U) -> App<Stack<M, U>, T, Err> {
|
||||
pub fn wrap<U>(self, mw: U) -> App<WebStack<M, U, Err>, T, Err> {
|
||||
App {
|
||||
middleware: Stack::new(self.middleware, mw),
|
||||
middleware: WebStack::new(self.middleware, mw),
|
||||
filter: self.filter,
|
||||
state_factories: self.state_factories,
|
||||
services: self.services,
|
||||
|
|
|
@ -707,16 +707,14 @@ mod tests {
|
|||
let req = TestRequest::default().to_http_request();
|
||||
|
||||
use crate::util::timeout::TimeoutError;
|
||||
let resp = WebResponseError::<DefaultError>::error_response(
|
||||
&TimeoutError::<UrlencodedError>::Timeout,
|
||||
&req,
|
||||
);
|
||||
let resp =
|
||||
Error::from(TimeoutError::<UrlencodedError>::Timeout).error_response(&req);
|
||||
assert_eq!(resp.status(), StatusCode::GATEWAY_TIMEOUT);
|
||||
|
||||
let resp = WebResponseError::<DefaultError>::error_response(
|
||||
&TimeoutError::<UrlencodedError>::Service(UrlencodedError::Chunked),
|
||||
&req,
|
||||
);
|
||||
let resp = Error::from(TimeoutError::<UrlencodedError>::Service(
|
||||
UrlencodedError::Chunked,
|
||||
))
|
||||
.error_response(&req);
|
||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||
|
||||
let resp = WebResponseError::<DefaultError>::error_response(
|
||||
|
|
|
@ -83,11 +83,14 @@ impl fmt::Debug for Error {
|
|||
}
|
||||
|
||||
/// Return `GATEWAY_TIMEOUT` for `TimeoutError`
|
||||
impl<E: WebResponseError<DefaultError>> WebResponseError<DefaultError> for TimeoutError<E> {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match self {
|
||||
TimeoutError::Service(e) => e.status_code(),
|
||||
TimeoutError::Timeout => StatusCode::GATEWAY_TIMEOUT,
|
||||
impl<E> From<TimeoutError<E>> for Error
|
||||
where
|
||||
Error: From<E>,
|
||||
{
|
||||
fn from(err: TimeoutError<E>) -> Error {
|
||||
match err {
|
||||
TimeoutError::Service(e) => e.into(),
|
||||
TimeoutError::Timeout => super::error::ErrorGatewayTimeout("").into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,6 +82,7 @@ mod route;
|
|||
mod scope;
|
||||
mod server;
|
||||
mod service;
|
||||
mod stack;
|
||||
pub mod test;
|
||||
pub mod types;
|
||||
mod util;
|
||||
|
|
|
@ -5,18 +5,16 @@ use crate::router::{IntoPattern, ResourceDef};
|
|||
use crate::service::boxed::{self, BoxService, BoxServiceFactory};
|
||||
use crate::service::dev::{AndThen, ServiceChain, ServiceChainFactory};
|
||||
use crate::service::{chain, chain_factory, ServiceCtx};
|
||||
use crate::service::{
|
||||
Identity, IntoServiceFactory, Middleware, Service, ServiceFactory, Stack,
|
||||
};
|
||||
use crate::service::{Identity, IntoServiceFactory, Middleware, Service, ServiceFactory};
|
||||
use crate::util::Extensions;
|
||||
|
||||
use super::dev::{insert_slash, WebServiceConfig, WebServiceFactory};
|
||||
use super::extract::FromRequest;
|
||||
use super::handler::Handler;
|
||||
use super::request::WebRequest;
|
||||
use super::response::WebResponse;
|
||||
use super::route::{IntoRoutes, Route, RouteService};
|
||||
use super::stack::WebStack;
|
||||
use super::{app::Filter, error::ErrorRenderer, guard::Guard, service::AppState};
|
||||
use super::{request::WebRequest, response::WebResponse};
|
||||
|
||||
type HttpService<Err: ErrorRenderer> =
|
||||
BoxService<WebRequest<Err>, WebResponse, Err::Container>;
|
||||
|
@ -276,9 +274,9 @@ where
|
|||
/// type (i.e modify response's body).
|
||||
///
|
||||
/// **Note**: middlewares get called in opposite order of middlewares registration.
|
||||
pub fn wrap<U>(self, mw: U) -> Resource<Err, Stack<M, U>, T> {
|
||||
pub fn wrap<U>(self, mw: U) -> Resource<Err, WebStack<M, U, Err>, T> {
|
||||
Resource {
|
||||
middleware: Stack::new(self.middleware, mw),
|
||||
middleware: WebStack::new(self.middleware, mw),
|
||||
filter: self.filter,
|
||||
rdef: self.rdef,
|
||||
name: self.name,
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::http::Response;
|
|||
use crate::router::{IntoPattern, ResourceDef, Router};
|
||||
use crate::service::boxed::{self, BoxService, BoxServiceFactory};
|
||||
use crate::service::{chain_factory, dev::ServiceChainFactory, IntoServiceFactory};
|
||||
use crate::service::{Identity, Middleware, Service, ServiceCtx, ServiceFactory, Stack};
|
||||
use crate::service::{Identity, Middleware, Service, ServiceCtx, ServiceFactory};
|
||||
use crate::util::{join, Extensions};
|
||||
|
||||
use super::app::Filter;
|
||||
|
@ -18,6 +18,7 @@ use super::response::WebResponse;
|
|||
use super::rmap::ResourceMap;
|
||||
use super::route::Route;
|
||||
use super::service::{AppServiceFactory, AppState, ServiceFactoryWrapper};
|
||||
use super::stack::WebStack;
|
||||
|
||||
type Guards = Vec<Box<dyn Guard>>;
|
||||
type HttpService<Err: ErrorRenderer> =
|
||||
|
@ -342,9 +343,9 @@ where
|
|||
/// WebResponse.
|
||||
///
|
||||
/// Use middleware when you need to read or modify *every* request in some way.
|
||||
pub fn wrap<U>(self, mw: U) -> Scope<Err, Stack<M, U>, T> {
|
||||
pub fn wrap<U>(self, mw: U) -> Scope<Err, WebStack<M, U, Err>, T> {
|
||||
Scope {
|
||||
middleware: Stack::new(self.middleware, mw),
|
||||
middleware: WebStack::new(self.middleware, mw),
|
||||
filter: self.filter,
|
||||
rdef: self.rdef,
|
||||
state: self.state,
|
||||
|
|
78
ntex/src/web/stack.rs
Normal file
78
ntex/src/web/stack.rs
Normal file
|
@ -0,0 +1,78 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use crate::service::{Middleware, Service, ServiceCtx};
|
||||
use crate::web::{ErrorRenderer, WebRequest, WebResponse};
|
||||
|
||||
/// Stack of middlewares.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WebStack<Inner, Outer, Err> {
|
||||
inner: Inner,
|
||||
outer: Outer,
|
||||
err: PhantomData<Err>,
|
||||
}
|
||||
|
||||
impl<Inner, Outer, Err> WebStack<Inner, Outer, Err> {
|
||||
pub fn new(inner: Inner, outer: Outer) -> Self {
|
||||
WebStack {
|
||||
inner,
|
||||
outer,
|
||||
err: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, Inner, Outer, Err> Middleware<S> for WebStack<Inner, Outer, Err>
|
||||
where
|
||||
Inner: Middleware<S>,
|
||||
Outer: Middleware<Inner::Service>,
|
||||
Outer::Service: Service<WebRequest<Err>, Response = WebResponse>,
|
||||
{
|
||||
type Service = WebMiddleware<Outer::Service, Err>;
|
||||
|
||||
fn create(&self, service: S) -> Self::Service {
|
||||
WebMiddleware {
|
||||
svc: self.outer.create(self.inner.create(service)),
|
||||
err: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WebMiddleware<S, Err> {
|
||||
svc: S,
|
||||
err: PhantomData<Err>,
|
||||
}
|
||||
|
||||
impl<S, Err> Clone for WebMiddleware<S, Err>
|
||||
where
|
||||
S: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
svc: self.svc.clone(),
|
||||
err: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, Err> Service<WebRequest<Err>> for WebMiddleware<S, Err>
|
||||
where
|
||||
S: Service<WebRequest<Err>, Response = WebResponse>,
|
||||
Err: ErrorRenderer,
|
||||
Err::Container: From<S::Error>,
|
||||
{
|
||||
type Response = WebResponse;
|
||||
type Error = Err::Container;
|
||||
|
||||
#[inline]
|
||||
async fn call(
|
||||
&self,
|
||||
req: WebRequest<Err>,
|
||||
ctx: ServiceCtx<'_, Self>,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
ctx.call(&self.svc, req).await.map_err(Into::into)
|
||||
}
|
||||
|
||||
crate::forward_ready!(svc);
|
||||
crate::forward_shutdown!(svc);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue