mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-14 16:47:19 +00:00
Create example of file_and_error handler for Axum, and create <ErrorBoundary/>
for leptos
This commit is contained in:
parent
300cc4f54c
commit
3215e44c9a
8 changed files with 75 additions and 10 deletions
|
@ -5,22 +5,26 @@ if #[cfg(feature = "ssr")] {
|
||||||
use axum::{
|
use axum::{
|
||||||
body::{boxed, Body, BoxBody},
|
body::{boxed, Body, BoxBody},
|
||||||
extract::Extension,
|
extract::Extension,
|
||||||
|
response::IntoResponse,
|
||||||
http::{Request, Response, StatusCode, Uri},
|
http::{Request, Response, StatusCode, Uri},
|
||||||
};
|
};
|
||||||
|
use axum::response::Response as AxumResponse;
|
||||||
use tower::ServiceExt;
|
use tower::ServiceExt;
|
||||||
use tower_http::services::ServeDir;
|
use tower_http::services::ServeDir;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use leptos::LeptosOptions;
|
use leptos::{LeptosOptions, view};
|
||||||
|
use crate::todo::{ErrorBoundary, ErrorBoundaryProps};
|
||||||
pub async fn file_handler(uri: Uri, Extension(options): Extension<Arc<LeptosOptions>>) -> Result<Response<BoxBody>, (StatusCode, String)> {
|
|
||||||
|
|
||||||
|
pub async fn file_and_error_handler(uri: Uri, Extension(options): Extension<Arc<LeptosOptions>>, req: Request<Body>) -> AxumResponse {
|
||||||
let options = &*options;
|
let options = &*options;
|
||||||
let root = options.site_root.clone();
|
let root = options.site_root.clone();
|
||||||
let res = get_static_file(uri.clone(), &root).await?;
|
let res = get_static_file(uri.clone(), &root).await.unwrap();
|
||||||
|
|
||||||
match res.status() {
|
if res.status() == StatusCode::OK {
|
||||||
StatusCode::OK => Ok(res),
|
res.into_response()
|
||||||
_ => Err((res.status(), "File Not Found".to_string()))
|
} else{
|
||||||
|
let handler = leptos_axum::render_app_to_stream(options.to_owned(), |cx| view! { cx, <ErrorBoundary/> });
|
||||||
|
handler(req).await.into_response()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,5 +41,7 @@ if #[cfg(feature = "ssr")] {
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ if #[cfg(feature = "ssr")] {
|
||||||
};
|
};
|
||||||
use crate::todo::*;
|
use crate::todo::*;
|
||||||
use todo_app_sqlite_axum::*;
|
use todo_app_sqlite_axum::*;
|
||||||
use crate::file::file_handler;
|
use crate::file::file_and_error_handler;
|
||||||
use leptos_axum::{generate_route_list, LeptosRoutes};
|
use leptos_axum::{generate_route_list, LeptosRoutes};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ if #[cfg(feature = "ssr")] {
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/api/*fn_name", post(leptos_axum::handle_server_fns))
|
.route("/api/*fn_name", post(leptos_axum::handle_server_fns))
|
||||||
.leptos_routes(leptos_options.clone(), routes, |cx| view! { cx, <TodoApp/> } )
|
.leptos_routes(leptos_options.clone(), routes, |cx| view! { cx, <TodoApp/> } )
|
||||||
.fallback(file_handler)
|
.fallback(file_and_error_handler)
|
||||||
.layer(Extension(Arc::new(leptos_options)));
|
.layer(Extension(Arc::new(leptos_options)));
|
||||||
|
|
||||||
// run our app with hyper
|
// run our app with hyper
|
||||||
|
|
|
@ -105,6 +105,17 @@ pub async fn delete_todo(id: u16) -> Result<(), ServerFnError> {
|
||||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))
|
.map_err(|e| ServerFnError::ServerError(e.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn ErrorBoundary(cx: Scope) -> impl IntoView {
|
||||||
|
provide_meta_context(cx);
|
||||||
|
view! {
|
||||||
|
cx,
|
||||||
|
<Link rel="shortcut icon" type_="image/ico" href="/favicon.ico"/>
|
||||||
|
<Stylesheet id="leptos" href="/pkg/todo_app_sqlite_axum.css"/>
|
||||||
|
<h1>"ERROR"</h1>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn TodoApp(cx: Scope) -> impl IntoView {
|
pub fn TodoApp(cx: Scope) -> impl IntoView {
|
||||||
provide_meta_context(cx);
|
provide_meta_context(cx);
|
||||||
|
|
|
@ -485,6 +485,10 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// impl IntoResponse for Pin<Box<dyn Future<Output = Response<StreamBody<PinnedHtmlStream>>>>>{
|
||||||
|
// todo!()
|
||||||
|
// }
|
||||||
|
|
||||||
/// Generates a list of all routes defined in Leptos's Router in your app. We can then use this to automatically
|
/// Generates a list of all routes defined in Leptos's Router in your app. We can then use this to automatically
|
||||||
/// create routes in Axum's Router without having to use wildcard matching or fallbacks. Takes in your root app Element
|
/// create routes in Axum's Router without having to use wildcard matching or fallbacks. Takes in your root app Element
|
||||||
/// as an argument so it can walk you app tree. This version is tailored to generate Axum compatible paths.
|
/// as an argument so it can walk you app tree. This version is tailored to generate Axum compatible paths.
|
||||||
|
|
29
leptos/src/error_boundary.rs
Normal file
29
leptos/src/error_boundary.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
use leptos_dom::{Errors, Fragment, IntoView, View};
|
||||||
|
use leptos_macro::component;
|
||||||
|
use leptos_reactive::{create_rw_signal, provide_context, RwSignal, Scope};
|
||||||
|
|
||||||
|
#[component(transparent)]
|
||||||
|
pub fn ErrorBoundary<F>(
|
||||||
|
cx: Scope,
|
||||||
|
/// The components inside the tag which will get rendered
|
||||||
|
children: Box<dyn FnOnce(Scope) -> Fragment>,
|
||||||
|
/// A fallback that will be shown if an error occurs.
|
||||||
|
fallback: F,
|
||||||
|
) -> impl IntoView
|
||||||
|
where
|
||||||
|
F: Fn(Scope, RwSignal<Errors>) -> View + 'static,
|
||||||
|
{
|
||||||
|
let errors: RwSignal<Errors> = create_rw_signal(cx, Errors::default());
|
||||||
|
|
||||||
|
provide_context(cx, errors);
|
||||||
|
|
||||||
|
// Run children so that they render and execute resources
|
||||||
|
let children = children(cx);
|
||||||
|
|
||||||
|
move || match errors.get().0.is_empty() {
|
||||||
|
true => children.clone(),
|
||||||
|
false => fallback(cx, errors).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl IntoView for Result<(), Box<dyn Error>> {}
|
|
@ -138,7 +138,8 @@ pub use leptos_server::*;
|
||||||
|
|
||||||
pub use tracing;
|
pub use tracing;
|
||||||
pub use typed_builder;
|
pub use typed_builder;
|
||||||
|
mod error_boundary;
|
||||||
|
pub use error_boundary::*;
|
||||||
mod for_loop;
|
mod for_loop;
|
||||||
pub use for_loop::*;
|
pub use for_loop::*;
|
||||||
mod suspense;
|
mod suspense;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
mod dyn_child;
|
mod dyn_child;
|
||||||
mod each;
|
mod each;
|
||||||
|
mod errors;
|
||||||
mod fragment;
|
mod fragment;
|
||||||
mod unit;
|
mod unit;
|
||||||
|
|
||||||
|
@ -11,6 +12,7 @@ use crate::{
|
||||||
use crate::{mount_child, prepare_to_move, MountKind, Mountable};
|
use crate::{mount_child, prepare_to_move, MountKind, Mountable};
|
||||||
pub use dyn_child::*;
|
pub use dyn_child::*;
|
||||||
pub use each::*;
|
pub use each::*;
|
||||||
|
pub use errors::*;
|
||||||
pub use fragment::*;
|
pub use fragment::*;
|
||||||
use leptos_reactive::Scope;
|
use leptos_reactive::Scope;
|
||||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||||
|
|
12
leptos_dom/src/components/errors.rs
Normal file
12
leptos_dom/src/components/errors.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
use crate::{IntoView, Transparent};
|
||||||
|
use std::{error::Error, sync::Arc};
|
||||||
|
|
||||||
|
/// A struct to hold all the possible errors that could be provided by child Views
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct Errors(pub Vec<Arc<dyn Error>>);
|
||||||
|
|
||||||
|
impl IntoView for Errors {
|
||||||
|
fn into_view(self, cx: leptos_reactive::Scope) -> crate::View {
|
||||||
|
Transparent::new(self).into_view(cx)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue