feat: middleware

This commit is contained in:
davidontop 2024-07-05 10:11:17 +02:00
parent 7c76a4a04e
commit 4e65532679
Signed by: DavidOnTop
GPG key ID: 5D05538A45D5149F
11 changed files with 138 additions and 39 deletions

2
.gitignore vendored
View file

@ -1 +1 @@
/target
/target

View file

@ -1,11 +1,11 @@
condense_wildcard_suffixes = true
edition = "2021"
fn_single_line = true
format_code_in_doc_comments = true
format_macro_matchers = true
hard_tabs = true
match_block_trailing_comma = true
imports_granularity = "Crate"
newline_style = "Unix"
group_imports = "StdExternalCrate"
tab_spaces = 4
condense_wildcard_suffixes = true
edition = "2021"
fn_single_line = true
format_code_in_doc_comments = true
format_macro_matchers = true
hard_tabs = true
match_block_trailing_comma = true
imports_granularity = "Crate"
newline_style = "Unix"
group_imports = "StdExternalCrate"
tab_spaces = 4

3
Cargo.lock generated
View file

@ -641,7 +641,7 @@ dependencies = [
[[package]]
name = "leptos_reactive_axum"
version = "0.1.0"
version = "1.0.0"
dependencies = [
"axum",
"axum-test",
@ -652,6 +652,7 @@ dependencies = [
"serde_json",
"thiserror",
"tokio",
"tower",
]
[[package]]

View file

@ -3,7 +3,7 @@ members = ["macros", "."]
[package]
name = "leptos_reactive_axum"
version = "0.1.0"
version = "1.0.0"
edition = "2021"
description = "reactive context for axum handlers"
authors = ["davidontop <me@davidon.top>"]
@ -13,15 +13,19 @@ license = "MIT"
repository = "https://git.davidon.top/public/leptos_reactive_axum.git"
[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"] }
axum = "0.7"
scopeguard = "1.2.0"
thiserror = "1.0.61"
http = "1.1.0"
tower = { version = "0.4.13", optional = true }
[features]
default = ["middleware"]
nightly = ["leptos_reactive/nightly"]
macros = ["dep:leptos_reactive_axum_macros"]
middleware = ["dep:tower"]
[dev-dependencies]
axum-test = "15.1.0"

44
LICENSE
View file

@ -1,22 +1,22 @@
MIT License
Copyright (c) 2023 DavidOnTop
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
MIT License
Copyright (c) 2023 DavidOnTop
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -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.

View file

@ -1,6 +1,6 @@
[package]
name = "leptos_reactive_axum_macros"
version = "0.1.0"
version = "1.0.0"
edition = "2021"
description = "reactive context for axum handlers"
authors = ["davidontop <me@davidon.top>"]

View file

@ -1,6 +1,7 @@
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
/// WARNING: inserts .unwrap() into handlers to make types the same as in function arguments, use middleware to avoid this
#[proc_macro_attribute]
pub fn reactive(
attr: proc_macro::TokenStream,

View file

@ -1,9 +1,12 @@
pub mod error;
#[cfg(feature = "middleware")]
pub mod middleware;
use std::fmt::Debug;
use axum::{extract::FromRequestParts, http::request::Parts};
use error::ExtractionError;
#[cfg(feature = "macros")]
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`

52
src/middleware.rs Normal file
View 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
View 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"));
}