Fix fullstack axum integration docs (#3263)

* Fix broken fullstack doc tests

* include all features in doc test

* fix clippy
This commit is contained in:
Evan Almloff authored and GitHub committed 2024-12-02 11:35:13 -08:00
1 parent 354c4ce9cc
commit 9dc567255a
8 files changed
+51 -116

No files matched your search

+1
View File
@@ -89,6 +89,7 @@ args = [
"test", "test",
"--doc", "--doc",
"--workspace", "--workspace",
"--all-features",
] ]
private = true private = true
+1
View File
@@ -22,6 +22,7 @@ fn main() {
// Turn off rustfmt since we're doing layouts and routes in the same enum // Turn off rustfmt since we're doing layouts and routes in the same enum
#[derive(Routable, Clone, Debug, PartialEq)] #[derive(Routable, Clone, Debug, PartialEq)]
#[rustfmt::skip] #[rustfmt::skip]
#[allow(clippy::empty_line_after_outer_attr)]
enum Route { enum Route {
// Wrap Home in a Navbar Layout // Wrap Home in a Navbar Layout
#[layout(NavBar)] #[layout(NavBar)]
+5 -72
View File
@@ -73,9 +73,11 @@ First, make sure your `axum` dependency is optional and enabled by the server fe
[dependencies] [dependencies]
dioxus = { version = "*", features = ["fullstack"] } dioxus = { version = "*", features = ["fullstack"] }
axum = { version = "0.7.0", optional = true } axum = { version = "0.7.0", optional = true }
tokio = { version = "1.0", features = ["full"], optional = true }
dioxus-cli-config = { version = "*", optional = true }
[features] [features]
server = ["dioxus/server", "dep:axum"] server = ["dioxus/server", "dep:axum", "dep:tokio", "dioxus-cli-config"]
web = ["dioxus/web"] web = ["dioxus/web"]
``` ```
@@ -87,77 +89,8 @@ use dioxus::prelude::*;
// The entry point for the server // The entry point for the server
#[cfg(feature = "server")] #[cfg(feature = "server")]
fn main() { #[tokio::main]
// Get the address the server should run on. If the CLI is running, the CLI proxies fullstack into the main address async fn main() {
// and we use the generated address the CLI gives us
let address = dioxus_cli_config::fullstack_address_or_localhost();
// Set up the axum router
let router = axum::Router::new()
// You can add a dioxus application to the router with the `serve_dioxus_application` method
// This will add a fallback route to the router that will serve your component and server functions
.serve_dioxus_application(ServeConfigBuilder::default(), App);
// Finally, we can launch the server
let router = router.into_make_service();
let listener = tokio::net::TcpListener::bind(address).await.unwrap();
axum::serve(listener, router).await.unwrap();
}
// For any other platform, we just launch the app
#[cfg(not(feature = "server"))]
fn main() {
dioxus::launch(App);
}
#[component]
fn App() -> Element {
let mut meaning = use_signal(|| None);
rsx! {
h1 { "Meaning of life: {meaning:?}" }
button {
onclick: move |_| async move {
if let Ok(data) = get_meaning("life the universe and everything".into()).await {
meaning.set(data);
}
},
"Run a server function"
}
}
}
#[server]
async fn get_meaning(of: String) -> Result<Option<u32>, ServerFnError> {
Ok(of.contains("life").then(|| 42))
}
```
## Axum Integration
If you have an existing Axum router or you need more control over the server, you can use the [`DioxusRouterExt`](https://docs.rs/dioxus-fullstack/0.6.0-alpha.2/dioxus_fullstack/prelude/trait.DioxusRouterExt.html) trait to integrate with your existing Axum router.
First, make sure your `axum` dependency is optional and enabled by the server feature flag. Axum cannot be compiled to wasm, so if it is enabled by default, it will cause a compile error:
```toml
[dependencies]
dioxus = { version = "*", features = ["fullstack"] }
axum = { version = "0.7.0", optional = true }
[features]
server = ["dioxus/server", "dep:axum"]
web = ["dioxus/web"]
```
Then we can set up dioxus with the axum server:
```rust, no_run
#![allow(non_snake_case)]
use dioxus::prelude::*;
// The entry point for the server
#[cfg(feature = "server")]
fn main() {
// Get the address the server should run on. If the CLI is running, the CLI proxies fullstack into the main address // Get the address the server should run on. If the CLI is running, the CLI proxies fullstack into the main address
// and we use the generated address the CLI gives us // and we use the generated address the CLI gives us
let address = dioxus_cli_config::fullstack_address_or_localhost(); let address = dioxus_cli_config::fullstack_address_or_localhost();
+11 -3
View File
@@ -3,13 +3,16 @@ This method interacts with information from the current request. The request may
1. The initial SSR render if this method called from a [`Component`](dioxus_lib::prelude::component) or a [`server`](crate::prelude::server) function that is called during the initial render 1. The initial SSR render if this method called from a [`Component`](dioxus_lib::prelude::component) or a [`server`](crate::prelude::server) function that is called during the initial render
```rust ```rust
# use dioxus::prelude::*;
#[component] #[component]
fn PrintHtmlRequestInfo() -> Element { fn PrintHtmlRequestInfo() -> Element {
// The server context only exists on the server, so we need to put it behind a server_only! config // The server context only exists on the server, so we need to put it behind a server_only! config
server_only! { server_only! {
// Since we are calling this from a component, the server context that is returned will be from // Since we are calling this from a component, the server context that is returned will be from
// the html request for ssr rendering // the html request for ssr rendering
println!("headers are {:?}", server_context().request_parts().headers); let context = server_context();
let request_parts = context.request_parts();
println!("headers are {:?}", request_parts.headers);
} }
rsx! {} rsx! {}
} }
@@ -18,11 +21,14 @@ fn PrintHtmlRequestInfo() -> Element {
2. A request to a [`server`](crate::prelude::server) function called directly from the client (either on desktop/mobile or on the web frontend after the initial render) 2. A request to a [`server`](crate::prelude::server) function called directly from the client (either on desktop/mobile or on the web frontend after the initial render)
```rust ```rust
# use dioxus::prelude::*;
#[server] #[server]
async fn read_headers() -> Result<(), ServerFnError> { async fn read_headers() -> Result<(), ServerFnError> {
// Since we are calling this from a server function, the server context that is may be from the // Since we are calling this from a server function, the server context that is may be from the
// initial request or a request from the client // initial request or a request from the client
println!("headers are {:?}", server_context().request_parts().headers); let context = server_context();
let request_parts = context.request_parts();
println!("headers are {:?}", request_parts.headers);
Ok(()) Ok(())
} }
@@ -32,7 +38,9 @@ fn CallServerFunction() -> Element {
button { button {
// If you click the button, the server function will be called and the server context will be // If you click the button, the server function will be called and the server context will be
// from the client request // from the client request
onclick: move |_| read_headers().await, onclick: move |_| async {
_ = read_headers().await
},
"Call server function" "Call server function"
} }
} }
+14 -26
View File
@@ -40,16 +40,10 @@ impl ServeConfigBuilder {
/// # fn app() -> Element { todo!() } /// # fn app() -> Element { todo!() }
/// use dioxus::prelude::*; /// use dioxus::prelude::*;
/// ///
/// let mut cfg = dioxus::fullstack::Config::new();
///
/// // Only set the server config if the server feature is enabled
/// server_only! {
/// cfg = cfg.with_server_cfg(ServeConfigBuilder::default().incremental(IncrementalRendererConfig::default()));
/// }
///
/// // Finally, launch the app with the config /// // Finally, launch the app with the config
/// LaunchBuilder::new() /// LaunchBuilder::new()
/// .with_cfg(cfg) /// // Only set the server config if the server feature is enabled
/// .with_cfg(server_only!(ServeConfigBuilder::default().incremental(IncrementalRendererConfig::default())))
/// .launch(app); /// .launch(app);
/// ``` /// ```
pub fn incremental(mut self, cfg: dioxus_isrg::IncrementalRendererConfig) -> Self { pub fn incremental(mut self, cfg: dioxus_isrg::IncrementalRendererConfig) -> Self {
@@ -92,26 +86,20 @@ impl ServeConfigBuilder {
/// # fn app() -> Element { todo!() } /// # fn app() -> Element { todo!() }
/// use dioxus::prelude::*; /// use dioxus::prelude::*;
/// ///
/// let mut cfg = dioxus::fullstack::Config::new();
///
/// // Only set the server config if the server feature is enabled
/// server_only! {
/// cfg = cfg.with_server_cfg(ServeConfigBuilder::default().root_id("app"));
/// }
///
/// // You also need to set the root id in your web config
/// web! {
/// cfg = cfg.with_web_config(dioxus::web::Config::default().rootname("app"));
/// }
///
/// // And desktop config
/// desktop! {
/// cfg = cfg.with_desktop_config(dioxus::desktop::Config::default().with_root_name("app"));
/// }
///
/// // Finally, launch the app with the config /// // Finally, launch the app with the config
/// LaunchBuilder::new() /// LaunchBuilder::new()
/// .with_cfg(cfg) /// // Only set the server config if the server feature is enabled
/// .with_cfg(server_only! {
/// ServeConfigBuilder::default().root_id("app")
/// })
/// // You also need to set the root id in your web config
/// .with_cfg(web! {
/// dioxus::web::Config::default().rootname("app")
/// })
/// // And desktop config
/// .with_cfg(desktop! {
/// dioxus::desktop::Config::default().with_root_name("app")
/// })
/// .launch(app); /// .launch(app);
/// ``` /// ```
pub fn root_id(mut self, root_id: &'static str) -> Self { pub fn root_id(mut self, root_id: &'static str) -> Self {
+9 -6
View File
@@ -14,7 +14,10 @@
//! tokio::runtime::Runtime::new() //! tokio::runtime::Runtime::new()
//! .unwrap() //! .unwrap()
//! .block_on(async move { //! .block_on(async move {
//! let listener = tokio::net::TcpListener::bind("127.0.0.01:8080") //! // Get the address the server should run on. If the CLI is running, the CLI proxies fullstack into the main address
//! // and we use the generated address the CLI gives us
//! let address = dioxus_cli_config::fullstack_address_or_localhost();
//! let listener = tokio::net::TcpListener::bind(address)
//! .await //! .await
//! .unwrap(); //! .unwrap();
//! axum::serve( //! axum::serve(
@@ -82,7 +85,7 @@ pub trait DioxusRouterExt<S> {
/// # use dioxus_fullstack::prelude::*; /// # use dioxus_fullstack::prelude::*;
/// #[tokio::main] /// #[tokio::main]
/// async fn main() { /// async fn main() {
/// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); /// let addr = dioxus_cli_config::fullstack_address_or_localhost();
/// let router = axum::Router::new() /// let router = axum::Router::new()
/// // Register server functions routes with the default handler /// // Register server functions routes with the default handler
/// .register_server_functions() /// .register_server_functions()
@@ -107,7 +110,7 @@ pub trait DioxusRouterExt<S> {
/// # use std::sync::Arc; /// # use std::sync::Arc;
/// #[tokio::main] /// #[tokio::main]
/// async fn main() { /// async fn main() {
/// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); /// let addr = dioxus_cli_config::fullstack_address_or_localhost();
/// let router = axum::Router::new() /// let router = axum::Router::new()
/// // Register server functions routes with the default handler /// // Register server functions routes with the default handler
/// .register_server_functions_with_context(Arc::new(vec![Box::new(|| Box::new(1234567890u32))])) /// .register_server_functions_with_context(Arc::new(vec![Box::new(|| Box::new(1234567890u32))]))
@@ -127,7 +130,7 @@ pub trait DioxusRouterExt<S> {
/// # use dioxus_fullstack::prelude::*; /// # use dioxus_fullstack::prelude::*;
/// #[tokio::main] /// #[tokio::main]
/// async fn main() { /// async fn main() {
/// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); /// let addr = dioxus_cli_config::fullstack_address_or_localhost();
/// let router = axum::Router::new() /// let router = axum::Router::new()
/// // Server side render the application, serve static assets, and register server functions /// // Server side render the application, serve static assets, and register server functions
/// .serve_static_assets() /// .serve_static_assets()
@@ -152,7 +155,7 @@ pub trait DioxusRouterExt<S> {
/// # use dioxus_fullstack::prelude::*; /// # use dioxus_fullstack::prelude::*;
/// #[tokio::main] /// #[tokio::main]
/// async fn main() { /// async fn main() {
/// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); /// let addr = dioxus_cli_config::fullstack_address_or_localhost();
/// let router = axum::Router::new() /// let router = axum::Router::new()
/// // Server side render the application, serve static assets, and register server functions /// // Server side render the application, serve static assets, and register server functions
/// .serve_dioxus_application(ServeConfig::new().unwrap(), app) /// .serve_dioxus_application(ServeConfig::new().unwrap(), app)
@@ -358,7 +361,7 @@ impl RenderHandleState {
/// ///
/// #[tokio::main] /// #[tokio::main]
/// async fn main() { /// async fn main() {
/// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); /// let addr = dioxus_cli_config::fullstack_address_or_localhost();
/// let router = axum::Router::new() /// let router = axum::Router::new()
/// // Register server functions, etc. /// // Register server functions, etc.
/// // Note you can use `register_server_functions_with_context` /// // Note you can use `register_server_functions_with_context`
+10 -8
View File
@@ -161,10 +161,11 @@ mod server_fn_impl {
/// #[server] /// #[server]
/// async fn set_headers() -> Result<(), ServerFnError> { /// async fn set_headers() -> Result<(), ServerFnError> {
/// let server_context = server_context(); /// let server_context = server_context();
/// let cookies = server_context.response_parts() /// let response_parts = server_context.response_parts();
/// .headers() /// let cookies = response_parts
/// .headers
/// .get("Cookie") /// .get("Cookie")
/// .ok_or_else(|| ServerFnError::msg("failed to find Cookie header in the response"))?; /// .ok_or_else(|| ServerFnError::new("failed to find Cookie header in the response"))?;
/// println!("{:?}", cookies); /// println!("{:?}", cookies);
/// Ok(()) /// Ok(())
/// } /// }
@@ -185,8 +186,8 @@ mod server_fn_impl {
/// async fn set_headers() -> Result<(), ServerFnError> { /// async fn set_headers() -> Result<(), ServerFnError> {
/// let server_context = server_context(); /// let server_context = server_context();
/// server_context.response_parts_mut() /// server_context.response_parts_mut()
/// .headers_mut() /// .headers
/// .insert("Cookie", "dioxus=fullstack"); /// .insert("Cookie", http::HeaderValue::from_static("dioxus=fullstack"));
/// Ok(()) /// Ok(())
/// } /// }
/// ``` /// ```
@@ -205,10 +206,11 @@ mod server_fn_impl {
/// #[server] /// #[server]
/// async fn read_headers() -> Result<(), ServerFnError> { /// async fn read_headers() -> Result<(), ServerFnError> {
/// let server_context = server_context(); /// let server_context = server_context();
/// let id: &i32 = server_context.request_parts() /// let request_parts = server_context.request_parts();
/// let id: &i32 = request_parts
/// .extensions /// .extensions
/// .get() /// .get()
/// .ok_or_else(|| ServerFnError::msg("failed to find i32 extension in the request"))?; /// .ok_or_else(|| ServerFnError::new("failed to find i32 extension in the request"))?;
/// println!("{:?}", id); /// println!("{:?}", id);
/// Ok(()) /// Ok(())
/// } /// }
@@ -231,7 +233,7 @@ mod server_fn_impl {
/// let id: i32 = server_context.request_parts_mut() /// let id: i32 = server_context.request_parts_mut()
/// .extensions /// .extensions
/// .remove() /// .remove()
/// .ok_or_else(|| ServerFnError::msg("failed to find i32 extension in the request"))?; /// .ok_or_else(|| ServerFnError::new("failed to find i32 extension in the request"))?;
/// println!("{:?}", id); /// println!("{:?}", id);
/// Ok(()) /// Ok(())
/// } /// }
@@ -181,7 +181,6 @@ impl ImageAssetOptions {
/// # use manganis::{asset, Asset, ImageAssetOptions, ImageSize}; /// # use manganis::{asset, Asset, ImageAssetOptions, ImageSize};
/// const _: Asset = asset!("/assets/image.png", ImageAssetOptions::new().with_size(ImageSize::Manual { width: 512, height: 512 })); /// const _: Asset = asset!("/assets/image.png", ImageAssetOptions::new().with_size(ImageSize::Manual { width: 512, height: 512 }));
/// ``` /// ```
pub const fn with_size(self, size: ImageSize) -> Self { pub const fn with_size(self, size: ImageSize) -> Self {
Self { size, ..self } Self { size, ..self }
} }