-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add
AnyMiddleware
to minimize boilerplate for Axum middleware
If a consumer wants to use a middleware that's not already supported by Roadster, they need to implement the `Middleware` trait, which is extra boilerplate that could be annoying. Add `AnyMiddleware` struct that implements the `Middleware` trait, so consumers just need to provide the name of the middleware and the logic to build/configure it. Closes #470
- Loading branch information
1 parent
e86381e
commit 1786bf8
Showing
8 changed files
with
133 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,20 @@ | ||
use crate::app_state::AppState; | ||
use aide::axum::ApiRouter; | ||
use axum::extract::Request; | ||
use axum::middleware::Next; | ||
use axum::response::Response; | ||
use roadster::service::http::middleware::any::AnyMiddleware; | ||
use roadster::service::http::middleware::Middleware; | ||
use tracing::info; | ||
|
||
pub mod example; | ||
|
||
pub fn routes(parent: &str) -> ApiRouter<AppState> { | ||
ApiRouter::new().merge(example::routes(parent)) | ||
} | ||
|
||
pub(crate) async fn hello_world_middleware_fn(request: Request, next: Next) -> Response { | ||
info!("Running `hello-world` middleware"); | ||
|
||
next.run(request).await | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
use crate::app::context::AppContext; | ||
use crate::error::RoadsterResult; | ||
use crate::service::http::middleware::Middleware; | ||
use axum::routing::Route; | ||
use axum::Router; | ||
use axum_core::extract::{FromRef, Request}; | ||
use axum_core::response::IntoResponse; | ||
use std::convert::Infallible; | ||
use tower::{Layer, Service}; | ||
use typed_builder::TypedBuilder; | ||
|
||
#[derive(TypedBuilder)] | ||
pub struct AnyMiddleware<S, L> | ||
where | ||
S: Clone + Send + Sync + 'static, | ||
AppContext: FromRef<S>, | ||
// Layer constrains copied from https://docs.rs/axum/0.7.7/axum/routing/struct.Router.html#method.layer | ||
L: Layer<Route> + Clone + Send + 'static, | ||
L::Service: Service<Request> + Clone + Send + 'static, | ||
<L::Service as Service<Request>>::Response: IntoResponse + 'static, | ||
<L::Service as Service<Request>>::Error: Into<Infallible> + 'static, | ||
<L::Service as Service<Request>>::Future: Send + 'static, | ||
{ | ||
#[builder(setter(into))] | ||
name: String, | ||
#[builder(default, setter(strip_option))] | ||
enabled: Option<bool>, | ||
#[builder(default, setter(strip_option))] | ||
priority: Option<i32>, | ||
#[builder(setter(transform = |p: impl Fn(&S) -> L + Send + 'static| to_box_fn(p) ))] | ||
layer_provider: Box<dyn Fn(&S) -> L + Send>, | ||
} | ||
|
||
fn to_box_fn<S, L>(p: impl Fn(&S) -> L + Send + 'static) -> Box<dyn Fn(&S) -> L + Send> { | ||
Box::new(p) | ||
} | ||
|
||
impl<S, L> Middleware<S> for AnyMiddleware<S, L> | ||
where | ||
S: Clone + Send + Sync + 'static, | ||
AppContext: FromRef<S>, | ||
// Layer constrains copied from https://docs.rs/axum/0.7.7/axum/routing/struct.Router.html#method.layer | ||
L: Layer<Route> + Clone + Send + 'static, | ||
L::Service: Service<Request> + Clone + Send + 'static, | ||
<L::Service as Service<Request>>::Response: IntoResponse + 'static, | ||
<L::Service as Service<Request>>::Error: Into<Infallible> + 'static, | ||
<L::Service as Service<Request>>::Future: Send + 'static, | ||
{ | ||
fn name(&self) -> String { | ||
self.name.clone() | ||
} | ||
|
||
fn enabled(&self, state: &S) -> bool { | ||
let context = AppContext::from_ref(state); | ||
let config = context | ||
.config() | ||
.service | ||
.http | ||
.custom | ||
.middleware | ||
.custom | ||
.get(&self.name); | ||
if let Some(config) = config { | ||
config.common.enabled(state) | ||
} else { | ||
context | ||
.config() | ||
.service | ||
.http | ||
.custom | ||
.middleware | ||
.default_enable | ||
|| self.enabled.unwrap_or_default() | ||
} | ||
} | ||
|
||
fn priority(&self, state: &S) -> i32 { | ||
AppContext::from_ref(state) | ||
.config() | ||
.service | ||
.http | ||
.custom | ||
.middleware | ||
.custom | ||
.get(&self.name) | ||
.map(|config| config.common.priority) | ||
.unwrap_or_else(|| self.priority.unwrap_or_default()) | ||
} | ||
|
||
fn install(&self, router: Router, state: &S) -> RoadsterResult<Router> { | ||
let router = router.layer((self.layer_provider)(state)); | ||
|
||
Ok(router) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
pub mod any; | ||
pub mod catch_panic; | ||
pub mod compression; | ||
pub mod cors; | ||
|