change: migrate Axum integration to use `with_state` over `layer(Extension)` (#1032)

This commit is contained in:
Matt Crane 2023-05-14 03:37:39 -07:00 committed by GitHub
parent 904c2e8a67
commit add13fd6a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 76 additions and 45 deletions

View File

@ -10,12 +10,12 @@ crate-type = ["cdylib", "rlib"]
console_log = "1.0.0"
console_error_panic_hook = "0.1.7"
cfg-if = "1.0.0"
leptos = { path = "../../../leptos/leptos", default-features = false, features = [
leptos = { path = "../../leptos", default-features = false, features = [
"serde",
] }
leptos_axum = { path = "../../../leptos/integrations/axum", default-features = false, optional = true }
leptos_meta = { path = "../../../leptos/meta", default-features = false }
leptos_router = { path = "../../../leptos/router", default-features = false }
leptos_axum = { path = "../../integrations/axum", default-features = false, optional = true }
leptos_meta = { path = "../../meta", default-features = false }
leptos_router = { path = "../../router", default-features = false }
log = "0.4.17"
serde = { version = "1", features = ["derive"] }
simple_logger = "4.0.0"

View File

@ -3,7 +3,7 @@ use cfg_if::cfg_if;
cfg_if! { if #[cfg(feature = "ssr")] {
use axum::{
body::{boxed, Body, BoxBody},
extract::Extension,
extract::State,
response::IntoResponse,
http::{Request, Response, StatusCode, Uri},
};
@ -14,7 +14,7 @@ cfg_if! { if #[cfg(feature = "ssr")] {
use leptos::{LeptosOptions, view};
use crate::landing::App;
pub async fn file_and_error_handler(uri: Uri, Extension(options): Extension<Arc<LeptosOptions>>, req: Request<Body>) -> AxumResponse {
pub async fn file_and_error_handler(uri: Uri, State(options): State<Arc<LeptosOptions>>, req: Request<Body>) -> AxumResponse {
let options = &*options;
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();

View File

@ -5,7 +5,7 @@ cfg_if! { if #[cfg(feature = "ssr")] {
use crate::landing::*;
use axum::body::Body as AxumBody;
use axum::{
extract::{Extension, Path},
extract::{State, Path},
http::Request,
response::{IntoResponse, Response},
routing::{get, post},
@ -21,7 +21,7 @@ cfg_if! { if #[cfg(feature = "ssr")] {
#[cfg(feature = "ssr")]
async fn custom_handler(
Path(id): Path<String>,
Extension(options): Extension<Arc<LeptosOptions>>,
State(options): State<Arc<LeptosOptions>>,
req: Request<AxumBody>,
) -> Response {
let handler = leptos_axum::render_app_to_stream_with_context(
@ -44,7 +44,7 @@ async fn main() {
// Setting this to None means we'll be using cargo-leptos and its env vars
let conf = get_configuration(None).await.unwrap();
let leptos_options = conf.leptos_options;
let leptos_options = Arc::new(conf.leptos_options);
let addr = leptos_options.site_addr;
let routes = generate_route_list(|cx| view! { cx, <App/> }).await;
@ -58,7 +58,7 @@ async fn main() {
|cx| view! { cx, <App/> },
)
.fallback(file_and_error_handler)
.layer(Extension(Arc::new(leptos_options)));
.with_state(leptos_options);
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`

View File

@ -4,7 +4,7 @@ cfg_if! {
if #[cfg(feature = "ssr")] {
use axum::{
body::{boxed, Body, BoxBody},
extract::Extension,
extract::State,
response::IntoResponse,
http::{Request, Response, StatusCode, Uri},
};
@ -15,7 +15,7 @@ if #[cfg(feature = "ssr")] {
use leptos::{LeptosOptions};
use crate::error_template::error_template;
pub async fn file_and_error_handler(uri: Uri, Extension(options): Extension<Arc<LeptosOptions>>, req: Request<Body>) -> AxumResponse {
pub async fn file_and_error_handler(uri: Uri, State(options): State<Arc<LeptosOptions>>, req: Request<Body>) -> AxumResponse {
let options = &*options;
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();

View File

@ -7,7 +7,6 @@ if #[cfg(feature = "ssr")] {
use axum::{
Router,
routing::get,
extract::Extension,
};
use leptos_axum::{generate_route_list, LeptosRoutes};
use std::sync::Arc;
@ -18,7 +17,7 @@ if #[cfg(feature = "ssr")] {
use hackernews_axum::*;
let conf = get_configuration(Some("Cargo.toml")).await.unwrap();
let leptos_options = conf.leptos_options;
let leptos_options = Arc::new(conf.leptos_options);
let addr = leptos_options.site_addr;
let routes = generate_route_list(|cx| view! { cx, <App/> }).await;
@ -29,7 +28,7 @@ if #[cfg(feature = "ssr")] {
.route("/favicon.ico", get(file_and_error_handler))
.leptos_routes(leptos_options.clone(), routes, |cx| view! { cx, <App/> } )
.fallback(file_and_error_handler)
.layer(Extension(Arc::new(leptos_options)));
.with_state(leptos_options);
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`

View File

@ -4,7 +4,7 @@ cfg_if! {
if #[cfg(feature = "ssr")] {
use axum::{
body::{boxed, Body, BoxBody},
extract::Extension,
extract::State,
response::IntoResponse,
http::{Request, Response, StatusCode, Uri},
};
@ -16,7 +16,7 @@ if #[cfg(feature = "ssr")] {
use crate::error_template::ErrorTemplate;
use crate::errors::TodoAppError;
pub async fn file_and_error_handler(uri: Uri, Extension(options): Extension<Arc<LeptosOptions>>, req: Request<Body>) -> AxumResponse {
pub async fn file_and_error_handler(uri: Uri, State(options): State<Arc<LeptosOptions>>, req: Request<Body>) -> AxumResponse {
let options = &*options;
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();

View File

@ -6,7 +6,7 @@ if #[cfg(feature = "ssr")] {
use axum::{
response::{Response, IntoResponse},
routing::get,
extract::{Path, Extension, RawQuery},
extract::{Path, State, Extension, RawQuery},
http::{Request, header::HeaderMap},
body::Body as AxumBody,
Router,
@ -33,7 +33,7 @@ if #[cfg(feature = "ssr")] {
}, request).await
}
async fn leptos_routes_handler(Extension(pool): Extension<SqlitePool>, auth_session: AuthSession, Extension(options): Extension<Arc<LeptosOptions>>, req: Request<AxumBody>) -> Response{
async fn leptos_routes_handler(Extension(pool): Extension<SqlitePool>, auth_session: AuthSession, State(options): State<Arc<LeptosOptions>>, req: Request<AxumBody>) -> Response{
let handler = leptos_axum::render_app_to_stream_with_context((*options).clone(),
move |cx| {
provide_context(cx, auth_session.clone());
@ -68,7 +68,7 @@ if #[cfg(feature = "ssr")] {
// Setting this to None means we'll be using cargo-leptos and its env vars
let conf = get_configuration(None).await.unwrap();
let leptos_options = conf.leptos_options;
let leptos_options = Arc::new(conf.leptos_options);
let addr = leptos_options.site_addr;
let routes = generate_route_list(|cx| view! { cx, <TodoApp/> }).await;
@ -80,8 +80,8 @@ if #[cfg(feature = "ssr")] {
.layer(AuthSessionLayer::<User, i64, SessionSqlitePool, SqlitePool>::new(Some(pool.clone()))
.with_config(auth_config))
.layer(SessionLayer::new(session_store))
.layer(Extension(Arc::new(leptos_options)))
.layer(Extension(pool));
.layer(Extension(pool))
.with_state(leptos_options);
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`

View File

@ -3,7 +3,7 @@ use cfg_if::cfg_if;
cfg_if! { if #[cfg(feature = "ssr")] {
use axum::{
body::{boxed, Body, BoxBody},
extract::Extension,
extract::State,
response::IntoResponse,
http::{Request, Response, StatusCode, Uri},
};
@ -14,7 +14,7 @@ cfg_if! { if #[cfg(feature = "ssr")] {
use leptos::{LeptosOptions, view};
use crate::app::App;
pub async fn file_and_error_handler(uri: Uri, Extension(options): Extension<Arc<LeptosOptions>>, req: Request<Body>) -> AxumResponse {
pub async fn file_and_error_handler(uri: Uri, State(options): State<Arc<LeptosOptions>>, req: Request<Body>) -> AxumResponse {
let options = &*options;
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();

View File

@ -1,7 +1,7 @@
#[cfg(feature = "ssr")]
#[tokio::main]
async fn main() {
use axum::{extract::Extension, routing::post, Router};
use axum::{routing::post, Router};
use leptos::*;
use leptos_axum::{generate_route_list, LeptosRoutes};
use ssr_modes_axum::{app::*, fallback::file_and_error_handler};
@ -9,7 +9,7 @@ async fn main() {
let conf = get_configuration(None).await.unwrap();
let addr = conf.leptos_options.site_addr;
let leptos_options = conf.leptos_options;
let leptos_options = Arc::new(conf.leptos_options);
// Generate the list of routes in your Leptos App
let routes = generate_route_list(|cx| view! { cx, <App/> }).await;
@ -24,7 +24,7 @@ async fn main() {
|cx| view! { cx, <App/> },
)
.fallback(file_and_error_handler)
.layer(Extension(Arc::new(leptos_options)));
.with_state(leptos_options);
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`

View File

@ -4,7 +4,7 @@ cfg_if! {
if #[cfg(feature = "ssr")] {
use axum::{
body::{boxed, Body, BoxBody},
extract::Extension,
extract::State,
response::IntoResponse,
http::{Request, Response, StatusCode, Uri},
};
@ -16,7 +16,7 @@ if #[cfg(feature = "ssr")] {
use crate::error_template::ErrorTemplate;
use crate::errors::TodoAppError;
pub async fn file_and_error_handler(uri: Uri, Extension(options): Extension<Arc<LeptosOptions>>, req: Request<Body>) -> AxumResponse {
pub async fn file_and_error_handler(uri: Uri, State(options): State<Arc<LeptosOptions>>, req: Request<Body>) -> AxumResponse {
let options = &*options;
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();

View File

@ -5,7 +5,7 @@ cfg_if! {
use leptos::*;
use axum::{
routing::{post, get},
extract::{Extension, Path},
extract::{State, Path},
http::Request,
response::{IntoResponse, Response},
Router,
@ -18,7 +18,7 @@ cfg_if! {
use std::sync::Arc;
//Define a handler to test extractor with state
async fn custom_handler(Path(id): Path<String>, Extension(options): Extension<Arc<LeptosOptions>>, req: Request<AxumBody>) -> Response{
async fn custom_handler(Path(id): Path<String>, State(options): State<Arc<LeptosOptions>>, req: Request<AxumBody>) -> Response{
let handler = leptos_axum::render_app_to_stream_with_context((*options).clone(),
move |cx| {
provide_context(cx, id.clone());
@ -42,7 +42,7 @@ cfg_if! {
// Setting this to None means we'll be using cargo-leptos and its env vars
let conf = get_configuration(None).await.unwrap();
let leptos_options = conf.leptos_options;
let leptos_options = Arc::new(conf.leptos_options);
let addr = leptos_options.site_addr;
let routes = generate_route_list(|cx| view! { cx, <TodoApp/> }).await;
@ -52,7 +52,7 @@ cfg_if! {
.route("/special/:id", get(custom_handler))
.leptos_routes(leptos_options.clone(), routes, |cx| view! { cx, <TodoApp/> } )
.fallback(file_and_error_handler)
.layer(Extension(Arc::new(leptos_options)));
.with_state(leptos_options);
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`

View File

@ -1126,12 +1126,39 @@ where
}
}
/// This trait allows one to use your custom struct in Axum's router, provided it can provide the
/// `LeptosOptions` to use for the `LeptosRoutes` trait functions.
pub trait LeptosOptionProvider {
fn options(&self) -> LeptosOptions;
}
/// Implement `LeptosOptionProvider` trait for `LeptosOptions` itself.
impl LeptosOptionProvider for LeptosOptions {
fn options(&self) -> LeptosOptions {
self.clone()
}
}
/// Implement `LeptosOptionProvider` trait for any type wrapped in an Arc, if that type implements
/// `LeptosOptionProvider` as states in axum are often provided wrapped in an Arc.
impl<T> LeptosOptionProvider for Arc<T>
where
T: LeptosOptionProvider,
{
fn options(&self) -> LeptosOptions {
(**self).options()
}
}
/// This trait allows one to pass a list of routes and a render function to Axum's router, letting us avoid
/// having to use wildcards or manually define all routes in multiple places.
pub trait LeptosRoutes {
pub trait LeptosRoutes<OP>
where
OP: LeptosOptionProvider,
{
fn leptos_routes<IV>(
self,
options: LeptosOptions,
options: OP,
paths: Vec<RouteListing>,
app_fn: impl Fn(leptos::Scope) -> IV + Clone + Send + 'static,
) -> Self
@ -1140,7 +1167,7 @@ pub trait LeptosRoutes {
fn leptos_routes_with_context<IV>(
self,
options: LeptosOptions,
options: OP,
paths: Vec<RouteListing>,
additional_context: impl Fn(leptos::Scope) + 'static + Clone + Send,
app_fn: impl Fn(leptos::Scope) -> IV + Clone + Send + 'static,
@ -1154,16 +1181,20 @@ pub trait LeptosRoutes {
handler: H,
) -> Self
where
H: axum::handler::Handler<T, (), axum::body::Body>,
H: axum::handler::Handler<T, OP, axum::body::Body>,
T: 'static;
}
/// The default implementation of `LeptosRoutes` which takes in a list of paths, and dispatches GET requests
/// to those paths to Leptos's renderer.
impl LeptosRoutes for axum::Router {
impl<OP> LeptosRoutes<OP> for axum::Router<OP>
where
OP: LeptosOptionProvider + Clone + Send + Sync + 'static,
{
#[tracing::instrument(level = "info", fields(error), skip_all)]
fn leptos_routes<IV>(
self,
options: LeptosOptions,
options: OP,
paths: Vec<RouteListing>,
app_fn: impl Fn(leptos::Scope) -> IV + Clone + Send + 'static,
) -> Self
@ -1176,7 +1207,7 @@ impl LeptosRoutes for axum::Router {
#[tracing::instrument(level = "trace", fields(error), skip_all)]
fn leptos_routes_with_context<IV>(
self,
options: LeptosOptions,
options: OP,
paths: Vec<RouteListing>,
additional_context: impl Fn(leptos::Scope) + 'static + Clone + Send,
app_fn: impl Fn(leptos::Scope) -> IV + Clone + Send + 'static,
@ -1194,7 +1225,7 @@ impl LeptosRoutes for axum::Router {
match listing.mode() {
SsrMode::OutOfOrder => {
let s = render_app_to_stream_with_context(
options.clone(),
options.options(),
additional_context.clone(),
app_fn.clone(),
);
@ -1208,7 +1239,7 @@ impl LeptosRoutes for axum::Router {
}
SsrMode::PartiallyBlocked => {
let s = render_app_to_stream_with_context_and_replace_blocks(
options.clone(),
options.options(),
additional_context.clone(),
app_fn.clone(),
true
@ -1223,7 +1254,7 @@ impl LeptosRoutes for axum::Router {
}
SsrMode::InOrder => {
let s = render_app_to_stream_in_order_with_context(
options.clone(),
options.options(),
additional_context.clone(),
app_fn.clone(),
);
@ -1237,7 +1268,7 @@ impl LeptosRoutes for axum::Router {
}
SsrMode::Async => {
let s = render_app_async_with_context(
options.clone(),
options.options(),
additional_context.clone(),
app_fn.clone(),
);
@ -1263,7 +1294,7 @@ impl LeptosRoutes for axum::Router {
handler: H,
) -> Self
where
H: axum::handler::Handler<T, (), axum::body::Body>,
H: axum::handler::Handler<T, OP, axum::body::Body>,
T: 'static,
{
let mut router = self;
@ -1286,6 +1317,7 @@ impl LeptosRoutes for axum::Router {
router
}
}
#[tracing::instrument(level = "trace", fields(error), skip_all)]
fn get_leptos_pool() -> LocalPoolHandle {
static LOCAL_POOL: OnceCell<LocalPoolHandle> = OnceCell::new();