Leptos can now generate routes and provide them to the Axum router. More

testing and Actix version to come
This commit is contained in:
benwis 2023-01-06 19:52:38 -08:00
parent 63b1837315
commit 677e4f2540
6 changed files with 85 additions and 9 deletions

View file

@ -31,7 +31,7 @@ cfg_if! {
let addr = conf.leptos_options.site_address.clone();
println!("BEFFOORE");
let routes = generate_route_list(|cx| view! { cx, <TodoApp/> });
println!("HIIIIIIIIIIII");
println!("Routes: {:#?}", routes);
HttpServer::new(move || {

View file

@ -92,6 +92,7 @@ pub async fn delete_todo(id: u16) -> Result<(), ServerFnError> {
#[component]
pub fn TodoApp(cx: Scope) -> impl IntoView {
provide_meta_context(cx);
view! {
cx,
<Stylesheet id="leptos" href="./target/site/pkg/todo_app_sqlite.css"/>

View file

@ -1,6 +1,5 @@
use cfg_if::cfg_if;
use leptos::*;
// boilerplate to run in different modes
cfg_if! {
if #[cfg(feature = "ssr")] {
@ -13,12 +12,18 @@ if #[cfg(feature = "ssr")] {
use todo_app_sqlite_axum::*;
use http::StatusCode;
use tower_http::services::ServeDir;
use tokio::{sync::RwLock, task::spawn_blocking, task::LocalSet};
use leptos_axum::{generate_route_list, LeptosRoutes};
use std::sync::Arc;
#[tokio::main]
async fn main() {
simple_logger::init_with_level(log::Level::Debug).expect("couldn't initialize logging");
let mut conn = db().await.expect("couldn't connect to DB");
let conn = db().await.expect("couldn't connect to DB");
/* sqlx::migrate!()
.run(&mut conn)
.await
@ -53,13 +58,18 @@ if #[cfg(feature = "ssr")] {
)
}
let routes = generate_route_list(|cx| view! { cx, <TodoApp/> }).await;
println!("Routes_Outside: {:#?}",&routes);
// build our application with a route
let app = Router::new()
let mut app = Router::new()
.route("/api/*fn_name", post(leptos_axum::handle_server_fns))
.nest_service("/pkg", pkg_service) // Only need if using wasm-pack. Can be deleted if using cargo-leptos
.nest_service(&bundle_path, cargo_leptos_service) // Only needed if using cargo-leptos. Can be deleted if using wasm-pack and cargo-run
.nest_service("/static", static_service)
.fallback(leptos_axum::render_app_to_stream(leptos_options, |cx| view! { cx, <TodoApp/> }));
.leptos_routes(leptos_options, routes, |cx| view! { cx, <TodoApp/> } );
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`

View file

@ -39,9 +39,11 @@ cfg_if! {
pub async fn get_todos(cx: Scope) -> Result<Vec<Todo>, ServerFnError> {
// this is just an example of how to access server context injected in the handlers
// http::Request doesn't implement Clone, so more work will be needed to do use_context() on this
let req_parts = use_context::<leptos_axum::RequestParts>(cx).unwrap();
println!("\ncalling server fn");
let req_parts = use_context::<leptos_axum::RequestParts>(cx);
if let Some(req_parts) = req_parts{
println!("Uri = {:?}", req_parts.uri);
}
use futures::TryStreamExt;
@ -105,6 +107,7 @@ pub async fn delete_todo(id: u16) -> Result<(), ServerFnError> {
#[component]
pub fn TodoApp(cx: Scope) -> impl IntoView {
provide_meta_context(cx);
view! {
cx,
<Stylesheet id="leptos" href="./target/site/pkg/todo_app_sqlite_axum.css"/>

View file

@ -3,6 +3,7 @@ use axum::{
extract::Path,
http::{header::HeaderName, header::HeaderValue, HeaderMap, Request, StatusCode},
response::IntoResponse,
routing::get,
};
use futures::{Future, SinkExt, Stream, StreamExt};
use http::{header, method::Method, uri::Uri, version::Version, Response};
@ -11,7 +12,7 @@ use leptos::*;
use leptos_meta::MetaContext;
use leptos_router::*;
use std::{io, pin::Pin, sync::Arc};
use tokio::{sync::RwLock, task::spawn_blocking};
use tokio::{sync::RwLock, task::spawn_blocking, task::LocalSet};
/// A struct to hold the parts of the incoming Request. Since `http::Request` isn't cloneable, we're forced
/// to construct this for Leptos to use in Axum
@ -499,3 +500,64 @@ where
})
}
}
pub async fn generate_route_list<IV>(app_fn: impl FnOnce(Scope) -> IV + 'static) -> Vec<String>
where
IV: IntoView + 'static,
{
#[derive(Default, Clone, Debug)]
pub struct Routes(pub Arc<RwLock<Vec<String>>>);
let routes = Routes::default();
let routes_inner = routes.clone();
let local = LocalSet::new();
// Run the local task set.
local
.run_until(async move {
tokio::task::spawn_local(async move {
let routes = leptos_router::generate_route_list_inner(app_fn);
let mut writable = routes_inner.0.write().await;
*writable = routes;
})
.await
.unwrap();
})
.await;
let routes = routes.0.read().await.to_owned();
routes.iter().map(|s| s.replace("", "/")).collect()
}
pub trait LeptosRoutes {
fn leptos_routes<IV>(
self,
options: LeptosOptions,
paths: Vec<String>,
app_fn: impl Fn(leptos::Scope) -> IV + Clone + Send + 'static,
) -> Self
where
IV: IntoView + 'static;
}
impl LeptosRoutes for axum::Router {
fn leptos_routes<IV>(
self,
options: LeptosOptions,
paths: Vec<String>,
app_fn: impl Fn(leptos::Scope) -> IV + Clone + Send + 'static,
) -> Self
where
IV: IntoView + 'static,
{
let mut router = self;
for path in paths.iter() {
router = router.route(
path,
get(render_app_to_stream(options.clone(), app_fn.clone())),
);
}
router
}
}

View file

@ -9,7 +9,7 @@ pub struct PossibleBranchContext(pub(crate) Rc<RefCell<Vec<Branch>>>);
/// Generates a list of all routes this application could possibly serve.
#[cfg(feature = "ssr")]
pub fn generate_route_list<IV>(app_fn: impl FnOnce(Scope) -> IV + 'static) -> Vec<String>
pub fn generate_route_list_inner<IV>(app_fn: impl FnOnce(Scope) -> IV + 'static) -> Vec<String>
where
IV: IntoView + 'static,
{