Properly handle router errors

Fixes file serving, including favicon
This commit is contained in:
Ethan Girouard 2024-03-01 15:04:15 -05:00
parent dcd08ff46e
commit fe465e2246
Signed by: eta357
GPG Key ID: 7BCDC36DFD11C146
3 changed files with 83 additions and 2 deletions

View File

@ -6,6 +6,7 @@ use leptos_meta::*;
use leptos_router::*; use leptos_router::*;
use crate::pages::login::*; use crate::pages::login::*;
use crate::pages::signup::*; use crate::pages::signup::*;
use crate::error_template::{AppError, ErrorTemplate};
#[component] #[component]
pub fn App() -> impl IntoView { pub fn App() -> impl IntoView {
@ -21,11 +22,17 @@ pub fn App() -> impl IntoView {
<Title text="LibreTunes"/> <Title text="LibreTunes"/>
// content for this welcome page // content for this welcome page
<Router> <Router fallback=|| {
let mut outside_errors = Errors::default();
outside_errors.insert_with_default_key(AppError::NotFound);
view! {
<ErrorTemplate outside_errors/>
}
.into_view()
}>
<main> <main>
<Routes> <Routes>
<Route path="" view=HomePage/> <Route path="" view=HomePage/>
<Route path="/*any" view=NotFound/>
<Route path="/login" view=Login /> <Route path="/login" view=Login />
<Route path="/signup" view=Signup /> <Route path="/signup" view=Signup />
</Routes> </Routes>

73
src/error_template.rs Normal file
View File

@ -0,0 +1,73 @@
use http::status::StatusCode;
use leptos::*;
use thiserror::Error;
#[cfg(feature = "ssr")]
use leptos_axum::ResponseOptions;
#[derive(Clone, Debug, Error)]
pub enum AppError {
#[error("Not Found")]
NotFound,
}
impl AppError {
pub fn status_code(&self) -> StatusCode {
match self {
AppError::NotFound => StatusCode::NOT_FOUND,
}
}
}
// A basic function to display errors served by the error boundaries.
// Feel free to do more complicated things here than just displaying the error.
#[component]
pub fn ErrorTemplate(
#[prop(optional)] outside_errors: Option<Errors>,
#[prop(optional)] errors: Option<RwSignal<Errors>>,
) -> impl IntoView {
let errors = match outside_errors {
Some(e) => create_rw_signal(e),
None => match errors {
Some(e) => e,
None => panic!("No Errors found and we expected errors!"),
},
};
// Get Errors from Signal
let errors = errors.get_untracked();
// Downcast lets us take a type that implements `std::error::Error`
let errors: Vec<AppError> = errors
.into_iter()
.filter_map(|(_k, v)| v.downcast_ref::<AppError>().cloned())
.collect();
// Only the response code for the first error is actually sent from the server
// this may be customized by the specific application
#[cfg(feature = "ssr")]
{
let response = use_context::<ResponseOptions>();
if let Some(response) = response {
response.set_status(errors[0].status_code());
}
}
view! {
<h1>{if errors.len() > 1 {"Errors"} else {"Error"}}</h1>
<For
// a function that returns the items we're iterating over; a signal is fine
each= move || {errors.clone().into_iter().enumerate()}
// a unique key for each item as a reference
key=|(index, _error)| *index
// renders each item to a view
children=move |error| {
let error_string = error.1.to_string();
let error_code= error.1.status_code();
view! {
<h2>{error_code.to_string()}</h2>
<p>"Error: " {error_string}</p>
}
}
/>
}
}

View File

@ -11,6 +11,7 @@ pub mod pages;
pub mod users; pub mod users;
pub mod search; pub mod search;
pub mod fileserv; pub mod fileserv;
pub mod error_template;
use cfg_if::cfg_if; use cfg_if::cfg_if;
cfg_if! { cfg_if! {