feat: middleware
This commit is contained in:
parent
7c76a4a04e
commit
4e65532679
11 changed files with 138 additions and 39 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1 +1 @@
|
|||
/target
|
||||
/target
|
||||
|
|
|
@ -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
3
Cargo.lock
generated
|
@ -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]]
|
||||
|
|
|
@ -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
44
LICENSE
|
@ -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.
|
||||
|
||||
|
|
|
@ -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]
|
||||
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>"]
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
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