feat: middleware
This commit is contained in:
parent
7c76a4a04e
commit
4e65532679
11 changed files with 138 additions and 39 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -641,7 +641,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "leptos_reactive_axum"
|
name = "leptos_reactive_axum"
|
||||||
version = "0.1.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"axum-test",
|
"axum-test",
|
||||||
|
@ -652,6 +652,7 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tower",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -3,7 +3,7 @@ members = ["macros", "."]
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "leptos_reactive_axum"
|
name = "leptos_reactive_axum"
|
||||||
version = "0.1.0"
|
version = "1.0.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "reactive context for axum handlers"
|
description = "reactive context for axum handlers"
|
||||||
authors = ["davidontop <me@davidon.top>"]
|
authors = ["davidontop <me@davidon.top>"]
|
||||||
|
@ -13,15 +13,19 @@ license = "MIT"
|
||||||
repository = "https://git.davidon.top/public/leptos_reactive_axum.git"
|
repository = "https://git.davidon.top/public/leptos_reactive_axum.git"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
leptos_reactive_axum_macros = { path = "./macros", version = "0" }
|
leptos_reactive_axum_macros = { path = "./macros", version = "0", optional = true }
|
||||||
leptos_reactive = { version = "0.6", features = ["ssr"] }
|
leptos_reactive = { version = "0.6", features = ["ssr"] }
|
||||||
axum = "0.7"
|
axum = "0.7"
|
||||||
scopeguard = "1.2.0"
|
scopeguard = "1.2.0"
|
||||||
thiserror = "1.0.61"
|
thiserror = "1.0.61"
|
||||||
http = "1.1.0"
|
http = "1.1.0"
|
||||||
|
tower = { version = "0.4.13", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
default = ["middleware"]
|
||||||
nightly = ["leptos_reactive/nightly"]
|
nightly = ["leptos_reactive/nightly"]
|
||||||
|
macros = ["dep:leptos_reactive_axum_macros"]
|
||||||
|
middleware = ["dep:tower"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
axum-test = "15.1.0"
|
axum-test = "15.1.0"
|
||||||
|
|
|
@ -1 +1,5 @@
|
||||||
# ctxt
|
# whoami
|
||||||
|
Library providing utilities for using leptos_reactive runtime inside axum handlers. Also automaticly uses provide_context to make the request Parts available inside any function called by the handler.
|
||||||
|
|
||||||
|
# But why?
|
||||||
|
When using something like the [rstml-component](https://crates.io/crates/rstml-component) crate it is usefull to be able to extract anything from parts inside any component. For example inside nav bar extract the path to be able to highlight active route. Additionally you can create a middleware layer that can add to the context things like IsLoggedIn or other information about the request that is used within more then one function/component called by the handler.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "leptos_reactive_axum_macros"
|
name = "leptos_reactive_axum_macros"
|
||||||
version = "0.1.0"
|
version = "1.0.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "reactive context for axum handlers"
|
description = "reactive context for axum handlers"
|
||||||
authors = ["davidontop <me@davidon.top>"]
|
authors = ["davidontop <me@davidon.top>"]
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
mod reactive;
|
mod reactive;
|
||||||
|
|
||||||
/// macro that when applied on an axum handler will provide leptos_reactive runtime and will allow you to use `leptos_reactive_axum::extract` by providing a context holding request parts
|
/// macro that when applied on an axum handler will provide leptos_reactive runtime and will allow you to use `leptos_reactive_axum::extract` by providing a context holding request parts
|
||||||
|
/// WARNING: inserts .unwrap() into handlers to make types the same as in function arguments, use middleware to avoid this
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn reactive(
|
pub fn reactive(
|
||||||
attr: proc_macro::TokenStream,
|
attr: proc_macro::TokenStream,
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
#[cfg(feature = "middleware")]
|
||||||
|
pub mod middleware;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use axum::{extract::FromRequestParts, http::request::Parts};
|
use axum::{extract::FromRequestParts, http::request::Parts};
|
||||||
use error::ExtractionError;
|
use error::ExtractionError;
|
||||||
|
#[cfg(feature = "macros")]
|
||||||
pub use leptos_reactive_axum_macros::reactive;
|
pub use leptos_reactive_axum_macros::reactive;
|
||||||
|
|
||||||
/// used to extract request parts from handlers, should be used in conjunction with `leptos_reactive_axum_macros::reactive macro`
|
/// used to extract request parts from handlers, should be used in conjunction with `leptos_reactive_axum_macros::reactive macro`
|
||||||
|
|
52
src/middleware.rs
Normal file
52
src/middleware.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
use std::{future::Future, pin::Pin};
|
||||||
|
|
||||||
|
use axum::{extract::Request, response::Response};
|
||||||
|
use tower::{Layer, Service};
|
||||||
|
|
||||||
|
/// ```rust
|
||||||
|
/// axum::Router::new().layer(ReactiveLayer);
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ReactiveLayer;
|
||||||
|
impl<S> Layer<S> for ReactiveLayer {
|
||||||
|
type Service = ReactiveMiddleware<S>;
|
||||||
|
|
||||||
|
fn layer(&self, inner: S) -> Self::Service {
|
||||||
|
ReactiveMiddleware {inner}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ReactiveMiddleware<S> {
|
||||||
|
inner: S,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Service<Request> for ReactiveMiddleware<S>
|
||||||
|
where
|
||||||
|
S: Service<Request, Response = Response> + Send + 'static,
|
||||||
|
S::Future: Send + 'static,
|
||||||
|
{
|
||||||
|
type Response = S::Response;
|
||||||
|
type Error = S::Error;
|
||||||
|
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
|
||||||
|
|
||||||
|
fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
|
||||||
|
self.inner.poll_ready(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, req: Request) -> Self::Future {
|
||||||
|
let __runtime = leptos_reactive::create_runtime();
|
||||||
|
let (parts, body) = req.into_parts();
|
||||||
|
leptos_reactive::provide_context(parts.clone());
|
||||||
|
let req = Request::from_parts(parts, body);
|
||||||
|
|
||||||
|
let future = self.inner.call(req);
|
||||||
|
Box::pin(async move {
|
||||||
|
let response: Response = future.await?;
|
||||||
|
|
||||||
|
__runtime.dispose();
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
34
tests/middleware.rs
Normal file
34
tests/middleware.rs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
use axum::{response::IntoResponse, routing::get, Json, Router};
|
||||||
|
use axum_test::TestServer;
|
||||||
|
use http::{HeaderMap, HeaderName, HeaderValue};
|
||||||
|
use leptos_reactive_axum::extract;
|
||||||
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
|
async fn handler(Json(json): Json<Value>) -> impl IntoResponse {
|
||||||
|
let headers: HeaderMap = extract().await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
headers.get("macrosareawesome"),
|
||||||
|
Some(&HeaderValue::from_static("YehTheyAre"))
|
||||||
|
);
|
||||||
|
assert_eq!(json.as_str(), Some("hello there"));
|
||||||
|
|
||||||
|
""
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handlers() {
|
||||||
|
let router = Router::new()
|
||||||
|
.route("/", get(handler))
|
||||||
|
.layer(leptos_reactive_axum::middleware::ReactiveLayer);
|
||||||
|
|
||||||
|
let server = TestServer::new(router).unwrap();
|
||||||
|
|
||||||
|
let _ = server
|
||||||
|
.get("/")
|
||||||
|
.add_header(
|
||||||
|
HeaderName::from_static("macrosareawesome"),
|
||||||
|
HeaderValue::from_static("YehTheyAre"),
|
||||||
|
)
|
||||||
|
.json(&json!("hello there"));
|
||||||
|
}
|
Loading…
Reference in a new issue