Skip to content

Commit

Permalink
Allow to wrap Handler with middlewares (#883)
Browse files Browse the repository at this point in the history
* Allow to wrap Handler with middlewares

* fix ci
  • Loading branch information
chrislearn committed Aug 23, 2024
1 parent 59783d5 commit 1fc0cf7
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 3 deletions.
62 changes: 62 additions & 0 deletions crates/core/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@
//! }
//! }
//! ```
use std::sync::Arc;

use crate::http::StatusCode;
use crate::{async_trait, Depot, FlowCtrl, Request, Response};

Expand Down Expand Up @@ -160,6 +162,11 @@ pub struct WhenHoop<H, F> {
pub inner: H,
pub filter: F,
}
impl<H, F> WhenHoop<H, F> {
pub fn new(inner: H, filter: F) -> Self {
Self { inner, filter }
}
}
#[async_trait]
impl<H, F> Handler for WhenHoop<H, F>
where
Expand Down Expand Up @@ -189,6 +196,61 @@ where
}
}

/// Handler that wrap [`Handler`] to let it use middlwares.
#[non_exhaustive]
pub struct HoopedHandler {
inner: Arc<dyn Handler>,
hoops: Vec<Arc<dyn Handler>>,
}
impl HoopedHandler {
/// Create new `HoopedHandler`.
pub fn new<H: Handler>(inner: H) -> Self {
Self {
inner: Arc::new(inner),
hoops: vec![],
}
}

/// Get current catcher's middlewares reference.
#[inline]
pub fn hoops(&self) -> &Vec<Arc<dyn Handler>> {
&self.hoops
}
/// Get current catcher's middlewares mutable reference.
#[inline]
pub fn hoops_mut(&mut self) -> &mut Vec<Arc<dyn Handler>> {
&mut self.hoops
}

/// Add a handler as middleware, it will run the handler when error catched.
#[inline]
pub fn hoop<H: Handler>(mut self, hoop: H) -> Self {
self.hoops.push(Arc::new(hoop));
self
}

/// Add a handler as middleware, it will run the handler when error catched.
///
/// This middleware only effective when the filter return true.
#[inline]
pub fn hoop_when<H, F>(mut self, hoop: H, filter: F) -> Self
where
H: Handler,
F: Fn(&Request, &Depot) -> bool + Send + Sync + 'static,
{
self.hoops.push(Arc::new(WhenHoop::new(hoop, filter)));
self
}
}
#[async_trait]
impl Handler for HoopedHandler {
async fn handle(&self, req: &mut Request, depot: &mut Depot, res: &mut Response, ctrl: &mut FlowCtrl) {
let inner: Arc<dyn Handler> = self.inner.clone();
ctrl.handlers.extend(self.hoops.iter().chain([&inner]).cloned());
ctrl.call_next(req, depot, res).await;
}
}

/// `none_skipper` will skipper nothing.
///
/// It can be used as default `Skipper` in middleware.
Expand Down
1 change: 1 addition & 0 deletions crates/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ where
}
}

#[doc(hidden)]
#[macro_export]
macro_rules! for_each_tuple {
($callback:ident) => {
Expand Down
29 changes: 27 additions & 2 deletions crates/serve-static/src/dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ use std::str::FromStr;
use std::time::SystemTime;

use salvo_core::fs::NamedFile;
use salvo_core::handler::{Handler, HoopedHandler};
use salvo_core::http::header::ACCEPT_ENCODING;
use salvo_core::http::{self, HeaderValue, Request, Response, StatusCode, StatusError};
use salvo_core::writing::Text;
use salvo_core::{async_trait, Depot, FlowCtrl, Handler, IntoVecString};
use salvo_core::{async_trait, Depot, FlowCtrl, IntoVecString};
use serde::{Deserialize, Serialize};
use serde_json::json;
use time::{macros::format_description, OffsetDateTime};
Expand Down Expand Up @@ -157,7 +158,7 @@ impl StaticDir {
compressed_variations.insert(CompressionAlgo::Gzip, vec!["gz".to_owned()]);
compressed_variations.insert(CompressionAlgo::Deflate, vec!["deflate".to_owned()]);

StaticDir {
Self {
roots: roots.collect(),
chunk_size: None,
include_dot_files: false,
Expand Down Expand Up @@ -240,6 +241,30 @@ impl StaticDir {
}
false
}

/// Wrap to `HoopedHandler`.
#[inline]
pub fn hooped<H: Handler>(self) -> HoopedHandler {
HoopedHandler::new(self)
}

/// Add a handler as middleware, it will run the handler when error catched.
#[inline]
pub fn hoop<H: Handler>(self, hoop: H) -> HoopedHandler {
HoopedHandler::new(self).hoop(hoop)
}

/// Add a handler as middleware, it will run the handler when error catched.
///
/// This middleware only effective when the filter return true.
#[inline]
pub fn hoop_when<H, F>(self, hoop: H, filter: F) -> HoopedHandler
where
H: Handler,
F: Fn(&Request, &Depot) -> bool + Send + Sync + 'static,
{
HoopedHandler::new(self).hoop_when(hoop, filter)
}
}
#[derive(Serialize, Deserialize, Debug)]
struct CurrentInfo {
Expand Down
27 changes: 26 additions & 1 deletion crates/serve-static/src/embed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use std::marker::PhantomData;
use rust_embed::{EmbeddedFile, Metadata, RustEmbed};
use salvo_core::http::header::{CONTENT_TYPE, ETAG, IF_NONE_MATCH};
use salvo_core::http::{HeaderValue, Mime, Request, Response, StatusCode};
use salvo_core::{async_trait, Depot, FlowCtrl, Handler, IntoVecString};
use salvo_core::{async_trait, Depot, FlowCtrl, IntoVecString};
use salvo_core::handler::{HoopedHandler, Handler};

use super::{decode_url_path_safely, format_url_path_safely, join_path, redirect_to_dir_url};

Expand Down Expand Up @@ -107,6 +108,30 @@ where
self.fallback = Some(fallback.into());
self
}

/// Wrap to `HoopedHandler`.
#[inline]
pub fn hooped<H: Handler>(self) -> HoopedHandler {
HoopedHandler::new(self)
}

/// Add a handler as middleware, it will run the handler when error catched.
#[inline]
pub fn hoop<H: Handler>(self, hoop: H) -> HoopedHandler {
HoopedHandler::new(self).hoop(hoop)
}

/// Add a handler as middleware, it will run the handler when error catched.
///
/// This middleware only effective when the filter return true.
#[inline]
pub fn hoop_when<H, F>(self, hoop: H, filter: F) -> HoopedHandler
where
H: Handler,
F: Fn(&Request, &Depot) -> bool + Send + Sync + 'static,
{
HoopedHandler::new(self).hoop_when(hoop, filter)
}
}
#[async_trait]
impl<T> Handler for StaticEmbed<T>
Expand Down

0 comments on commit 1fc0cf7

Please sign in to comment.