redirects, middleware, Transport, set/capture error status (#4751)
* wip: redirect * redirect example * fix file upload not in a form * wip * deny missing cfgs * wip: add router fallback but it's not working * fix suspense bug after rebuilds that queued work * more cleanup * add test for error codes * add helpful comment * bring back middleware annotation * fix cfg * add server feature * fix doctest * just disable liveview test for now * fix liveview * dont send init * cache more playwright! * add more pkgs to cache
This commit is contained in:
1 parent
8a81475ba8
commit
9243ef4128
35 files changed
+935
-800
No files matched your search
@@ -0,0 +1,77 @@
|
||||
//! To render custom error pages, you can create a layout component that captures errors from routes
|
||||
//! with an `ErrorBoundary` and display different content based on the error type.
|
||||
//!
|
||||
//! While capturing the error, we set the appropriate HTTP status code using `FullstackContext::commit_error_status`.
|
||||
//! The router will then use this status code when doing server-side rendering (SSR).
|
||||
//!
|
||||
//! Any errors not captured by an error boundary will be handled by dioxus-ssr itself, which will render
|
||||
//! a generic error page instead.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_fullstack::{FullstackContext, StatusCode};
|
||||
|
||||
fn main() {
|
||||
dioxus::launch(|| {
|
||||
rsx! {
|
||||
Router::<Route> {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Routable, PartialEq, Clone, Debug)]
|
||||
enum Route {
|
||||
#[layout(ErrorLayout)]
|
||||
#[route("/")]
|
||||
Home,
|
||||
|
||||
#[route("/blog/:id")]
|
||||
Blog { id: u32 },
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Home() -> Element {
|
||||
rsx! {
|
||||
div { "Welcome to the home page!" }
|
||||
div { display: "flex", flex_direction: "column",
|
||||
Link { to: Route::Blog { id: 1 }, "Go to blog post 1" }
|
||||
Link { to: Route::Blog { id: 2 }, "Go to blog post 2" }
|
||||
Link { to: Route::Blog { id: 3 }, "Go to blog post 3 (error)" }
|
||||
Link { to: Route::Blog { id: 4 }, "Go to blog post 4 (not found)" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Blog(id: u32) -> Element {
|
||||
match id {
|
||||
1 => rsx! { div { "Blog post 1" } },
|
||||
2 => rsx! { div { "Blog post 2" } },
|
||||
3 => dioxus_core::bail!("An error occurred while loading the blog post!"),
|
||||
_ => HttpError::not_found("Blog post not found")?,
|
||||
}
|
||||
}
|
||||
|
||||
/// In our `ErrorLayout` component, we wrap the `Outlet` in an `ErrorBoundary`. This lets us attempt
|
||||
/// to downcast the error to an `HttpError` and set the appropriate status code.
|
||||
///
|
||||
/// The `commit_error_status` function will attempt to downcast the error to an `HttpError` and
|
||||
/// set the status code accordingly. Note that you can commit any status code you want with `commit_http_status`.
|
||||
///
|
||||
/// The router will automatically set the HTTP status code when doing SSR.
|
||||
#[component]
|
||||
fn ErrorLayout() -> Element {
|
||||
rsx! {
|
||||
ErrorBoundary {
|
||||
handle_error: move |err: ErrorContext| {
|
||||
let http_error = FullstackContext::commit_error_status(err.error().unwrap());
|
||||
match http_error.status {
|
||||
StatusCode::NOT_FOUND => rsx! { div { "404 - Page not found" } },
|
||||
StatusCode::UNAUTHORIZED => rsx! { div { "401 - Unauthorized" } },
|
||||
StatusCode::INTERNAL_SERVER_ERROR => rsx! { div { "500 - Internal Server Error" } },
|
||||
_ => rsx! { div { "An unknown error occurred" } },
|
||||
}
|
||||
},
|
||||
Outlet::<Route> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
//! This example shows how to use middleware in a fullstack Dioxus app.
|
||||
//!
|
||||
//! Dioxus supports two ways of middleware:
|
||||
//! - Applying layers to the top-level axum router
|
||||
//! - Apply `#[middleware]` attributes to individual handlers
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
use {std::time::Duration, tower_http::timeout::TimeoutLayer};
|
||||
|
||||
fn main() {
|
||||
#[cfg(not(feature = "server"))]
|
||||
dioxus::launch(app);
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
dioxus::serve(|| async move {
|
||||
use axum::{extract::Request, middleware::Next};
|
||||
use dioxus::server::axum;
|
||||
|
||||
Ok(dioxus::server::router(app)
|
||||
// we can apply a layer to the entire router using axum's `.layer` method
|
||||
.layer(axum::middleware::from_fn(
|
||||
|request: Request, next: Next| async move {
|
||||
println!("Request: {} {}", request.method(), request.uri().path());
|
||||
let res = next.run(request).await;
|
||||
println!("Response: {}", res.status());
|
||||
res
|
||||
},
|
||||
)))
|
||||
});
|
||||
}
|
||||
|
||||
fn app() -> Element {
|
||||
let mut per_route = use_action(per_route_middleware);
|
||||
|
||||
rsx! {
|
||||
h1 { "Fullstack Middleware Example" }
|
||||
button { onclick: move |_| per_route.call(), "Fetch Data" }
|
||||
pre { "{per_route.result():#?}" }
|
||||
}
|
||||
}
|
||||
|
||||
// We can use the `#[middleware]` attribute to apply middleware to individual handlers.
|
||||
//
|
||||
// Here, we're applying a timeout to the `per_route_middleware` handler, which will return a 504
|
||||
// if the handler takes longer than 3 seconds to complete.
|
||||
//
|
||||
// To add multiple middleware layers, simply stack multiple `#[middleware]` attributes.
|
||||
#[get("/api/count")]
|
||||
#[middleware(TimeoutLayer::new(Duration::from_secs(3)))]
|
||||
async fn per_route_middleware() -> Result<String> {
|
||||
tokio::time::sleep(Duration::from_secs(5)).await;
|
||||
Ok("Hello, world!".to_string())
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//! This example shows how to use the axum `Redirect` type to redirect the client to a different URL.
|
||||
//!
|
||||
//! On the web, a redirect will not be handled directly by JS, but instead the browser will automatically
|
||||
//! follow the redirect. This is useful for redirecting to different pages after a form submission.
|
||||
//!
|
||||
//! Note that redirects returned to the client won't navigate the SPA to a new page automatically.
|
||||
//! For managing a session or auth with client side routing, you'll need to handle that in the SPA itself.
|
||||
|
||||
use dioxus::{fullstack::Redirect, prelude::*};
|
||||
|
||||
fn main() {
|
||||
dioxus::launch(|| {
|
||||
rsx! {
|
||||
Router::<Route> {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Routable)]
|
||||
enum Route {
|
||||
#[route("/")]
|
||||
Home,
|
||||
|
||||
#[route("/blog")]
|
||||
Blog,
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Home() -> Element {
|
||||
rsx! {
|
||||
h1 { "Welcome home" }
|
||||
form {
|
||||
method: "post",
|
||||
action: "/api/old-blog",
|
||||
button { "Go to blog" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Blog() -> Element {
|
||||
rsx! {
|
||||
h1 { "Welcome to the blog!" }
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/api/old-blog")]
|
||||
async fn redirect_to_blog() -> Result<Redirect> {
|
||||
Ok(Redirect::to("/blog"))
|
||||
}
|
||||
Reference in new issue
Block a user