mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-10 06:34:20 +00:00
fix other fullstack adapters
This commit is contained in:
parent
694bef0d93
commit
89b1e56fc3
9 changed files with 105 additions and 95 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -10966,16 +10966,11 @@ name = "warp-hello-world"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"dioxus",
|
||||
"dioxus-fullstack",
|
||||
"dioxus-web",
|
||||
"execute",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"simple_logger",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"tracing-wasm",
|
||||
"warp",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
//! Run with:
|
||||
//!
|
||||
//! ```sh
|
||||
//! dx build --features web --release
|
||||
//! cargo run --features ssr --release
|
||||
//! dx serve --platform fullstack
|
||||
//! ```
|
||||
|
||||
#![allow(non_snake_case, unused)]
|
||||
|
@ -10,14 +9,10 @@ use dioxus::prelude::*;
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
fn app() -> Element {
|
||||
// let state = use_server_future(|| async move { get_server_data().await.unwrap() })?;
|
||||
// let state = state.value();
|
||||
|
||||
let mut count = use_signal(|| 0);
|
||||
let text = use_signal(|| "...".to_string());
|
||||
|
||||
rsx! {
|
||||
// div { "Server state: {state}" }
|
||||
h1 { "High-Five counter: {count}" }
|
||||
button { onclick: move |_| count += 1, "Up high!" }
|
||||
button { onclick: move |_| count -= 1, "Down low!" }
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
//! Run with:
|
||||
//!
|
||||
//! ```sh
|
||||
//! dx build --features web --release
|
||||
//! cargo run --features ssr --release
|
||||
//! dx serve --platform fullstack
|
||||
//! ```
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
//! Run with:
|
||||
//!
|
||||
//! ```sh
|
||||
//! dx build --features web --release
|
||||
//! cargo run --features ssr --release
|
||||
//! dx serve --platform fullstack
|
||||
//! ```
|
||||
|
||||
#![allow(non_snake_case, unused)]
|
||||
|
|
|
@ -7,19 +7,14 @@ publish = false
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
dioxus-web = { workspace = true, features=["hydrate"], optional = true }
|
||||
dioxus = { workspace = true }
|
||||
dioxus-fullstack = { workspace = true }
|
||||
dioxus = { workspace = true, features = ["fullstack"] }
|
||||
serde = "1.0.159"
|
||||
warp = { version = "0.3.3", optional = true }
|
||||
execute = "0.2.12"
|
||||
reqwest = "0.11.18"
|
||||
simple_logger = "4.2.0"
|
||||
tracing-wasm = "0.2.1"
|
||||
tracing.workspace = true
|
||||
tracing-subscriber = "0.3.17"
|
||||
reqwest = "0.11.18"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
ssr = ["warp", "dioxus-fullstack/warp"]
|
||||
web = ["dioxus-web"]
|
||||
ssr = ["dioxus/warp"]
|
||||
web = ["dioxus/web"]
|
||||
|
|
|
@ -1,31 +1,17 @@
|
|||
//! Run with:
|
||||
//!
|
||||
//! ```sh
|
||||
//! dx build --features web --release
|
||||
//! cargo run --features ssr --release
|
||||
//! dx serve --platform fullstack
|
||||
//! ```
|
||||
|
||||
#![allow(non_snake_case, unused)]
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_fullstack::{launch, prelude::*};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Props, PartialEq, Debug, Default, Serialize, Deserialize, Clone)]
|
||||
struct AppProps {
|
||||
count: i32,
|
||||
}
|
||||
|
||||
fn app(cx: Scope<AppProps>) -> Element {
|
||||
let state =
|
||||
use_server_future((), |()| async move { get_server_data().await.unwrap() })?.value();
|
||||
|
||||
fn app() -> Element {
|
||||
let mut count = use_signal(|| 0);
|
||||
let text = use_signal(|| "...".to_string());
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
"Server state: {state}"
|
||||
}
|
||||
h1 { "High-Five counter: {count}" }
|
||||
button { onclick: move |_| count += 1, "Up high!" }
|
||||
button { onclick: move |_| count -= 1, "Down low!" }
|
||||
|
@ -46,14 +32,14 @@ fn app(cx: Scope<AppProps>) -> Element {
|
|||
}
|
||||
}
|
||||
|
||||
#[server(PostServerData)]
|
||||
#[server]
|
||||
async fn post_server_data(data: String) -> Result<(), ServerFnError> {
|
||||
println!("Server received: {}", data);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[server(GetServerData)]
|
||||
#[server]
|
||||
async fn get_server_data() -> Result<String, ServerFnError> {
|
||||
Ok(reqwest::get("https://httpbin.org/ip").await?.text().await?)
|
||||
}
|
||||
|
@ -64,5 +50,5 @@ fn main() {
|
|||
#[cfg(feature = "ssr")]
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
LaunchBuilder::new_with_props(app, AppProps { count: 0 }).launch()
|
||||
launch(app);
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
//! }
|
||||
//! ```
|
||||
|
||||
use dioxus_lib::prelude::*;
|
||||
use http_body_util::{BodyExt, Limited};
|
||||
use hyper::body::Body as HyperBody;
|
||||
use hyper::StatusCode;
|
||||
|
@ -201,10 +202,11 @@ pub trait DioxusRouterExt {
|
|||
///
|
||||
/// fn app() -> Element {todo!()}
|
||||
/// ```
|
||||
fn serve_dioxus_application<P: Clone + serde::Serialize + Send + Sync + 'static>(
|
||||
fn serve_dioxus_application(
|
||||
self,
|
||||
server_fn_path: &'static str,
|
||||
cfg: impl Into<ServeConfig<P>>,
|
||||
cfg: impl Into<ServeConfig>,
|
||||
virtual_dom_factory: impl Fn() -> VirtualDom + Send + Sync + 'static,
|
||||
) -> Self;
|
||||
}
|
||||
|
||||
|
@ -281,10 +283,11 @@ impl DioxusRouterExt for Router {
|
|||
self
|
||||
}
|
||||
|
||||
fn serve_dioxus_application<P: Clone + serde::Serialize + Send + Sync + 'static>(
|
||||
fn serve_dioxus_application(
|
||||
self,
|
||||
server_fn_path: &'static str,
|
||||
cfg: impl Into<ServeConfig<P>>,
|
||||
cfg: impl Into<ServeConfig>,
|
||||
virtual_dom_factory: impl Fn() -> VirtualDom + Send + Sync + 'static,
|
||||
) -> Self {
|
||||
let cfg = cfg.into();
|
||||
|
||||
|
@ -376,19 +379,26 @@ async fn convert_response(response: HyperResponse, res: &mut Response) {
|
|||
}
|
||||
|
||||
/// A handler that renders a Dioxus application to HTML using server-side rendering.
|
||||
pub struct SSRHandler<P: Clone> {
|
||||
cfg: ServeConfig<P>,
|
||||
pub struct SSRHandler {
|
||||
config: ServeConfig,
|
||||
virtual_dom: Box<dyn Fn() -> VirtualDom + Send + Sync>,
|
||||
}
|
||||
|
||||
impl<P: Clone> SSRHandler<P> {
|
||||
impl SSRHandler {
|
||||
/// Creates a new SSR handler with the given configuration.
|
||||
pub fn new(cfg: ServeConfig<P>) -> Self {
|
||||
Self { cfg }
|
||||
pub fn new(
|
||||
config: ServeConfig,
|
||||
virtual_dom: impl Fn() -> VirtualDom + Send + Sync + 'static,
|
||||
) -> Self {
|
||||
Self {
|
||||
config,
|
||||
virtual_dom: Box::new(virtual_dom),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<P: Clone + serde::Serialize + Send + Sync + 'static> Handler for SSRHandler<P> {
|
||||
impl Handler for SSRHandler {
|
||||
async fn handle(
|
||||
&self,
|
||||
req: &mut Request,
|
||||
|
|
|
@ -52,6 +52,7 @@ use crate::{
|
|||
};
|
||||
|
||||
use crate::server_fn_service;
|
||||
use dioxus_lib::prelude::VirtualDom;
|
||||
use server_fn::{Encoding, Payload, ServerFunctionRegistry};
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
|
@ -179,68 +180,85 @@ pub fn register_server_fns(server_fn_route: &'static str) -> BoxedFilter<(impl R
|
|||
/// todo!()
|
||||
/// }
|
||||
/// ```
|
||||
pub fn serve_dioxus_application<P: Clone + serde::Serialize + Send + Sync + 'static>(
|
||||
pub fn serve_dioxus_application(
|
||||
server_fn_route: &'static str,
|
||||
cfg: impl Into<ServeConfig<P>>,
|
||||
cfg: impl Into<ServeConfig>,
|
||||
virtual_dom_factory: impl Fn() -> VirtualDom + Send + Sync + 'static,
|
||||
) -> BoxedFilter<(impl Reply,)> {
|
||||
let cfg = cfg.into();
|
||||
// Serve the dist folder and the index.html file
|
||||
let serve_dir = warp::fs::dir(cfg.assets_path);
|
||||
|
||||
let virtual_dom_factory =
|
||||
Arc::new(virtual_dom_factory) as Arc<dyn Fn() -> VirtualDom + Send + Sync + 'static>;
|
||||
|
||||
connect_hot_reload()
|
||||
// First register the server functions
|
||||
.or(register_server_fns(server_fn_route))
|
||||
// Then the index route
|
||||
.or(path::end().and(render_ssr(cfg.clone())))
|
||||
.or(path::end().and(render_ssr(cfg.clone(), {
|
||||
let virtual_dom_factory = virtual_dom_factory.clone();
|
||||
move || virtual_dom_factory()
|
||||
})))
|
||||
// Then the static assets
|
||||
.or(serve_dir)
|
||||
// Then all other routes
|
||||
.or(render_ssr(cfg))
|
||||
.or(render_ssr(cfg, move || virtual_dom_factory()))
|
||||
.boxed()
|
||||
}
|
||||
|
||||
/// Server render the application.
|
||||
pub fn render_ssr<P: Clone + serde::Serialize + Send + Sync + 'static>(
|
||||
cfg: ServeConfig<P>,
|
||||
pub fn render_ssr(
|
||||
cfg: ServeConfig,
|
||||
virtual_dom_factory: impl Fn() -> VirtualDom + Send + Sync + 'static,
|
||||
) -> impl Filter<Extract = (impl Reply,), Error = warp::Rejection> + Clone {
|
||||
warp::get()
|
||||
.and(request_parts())
|
||||
.and(with_ssr_state(&cfg))
|
||||
.then(move |parts: http::request::Parts, renderer: SSRState| {
|
||||
let route = parts.uri.path().to_string();
|
||||
let parts = Arc::new(RwLock::new(parts));
|
||||
let cfg = cfg.clone();
|
||||
async move {
|
||||
let server_context = DioxusServerContext::new(parts);
|
||||
.and(with_ssr_state(&cfg, virtual_dom_factory))
|
||||
.then(
|
||||
move |parts: http::request::Parts,
|
||||
(renderer, virtual_dom_factory): (
|
||||
SSRState,
|
||||
Arc<dyn Fn() -> VirtualDom + Send + Sync + 'static>,
|
||||
)| {
|
||||
let route = parts.uri.path().to_string();
|
||||
let parts = Arc::new(RwLock::new(parts));
|
||||
let cfg = cfg.clone();
|
||||
async move {
|
||||
let server_context = DioxusServerContext::new(parts);
|
||||
|
||||
match renderer.render(route, &cfg, &server_context).await {
|
||||
Ok(rendered) => {
|
||||
let crate::render::RenderResponse { html, freshness } = rendered;
|
||||
match renderer
|
||||
.render(route, &cfg, move || virtual_dom_factory(), &server_context)
|
||||
.await
|
||||
{
|
||||
Ok(rendered) => {
|
||||
let crate::render::RenderResponse { html, freshness } = rendered;
|
||||
|
||||
let mut res = Response::builder()
|
||||
.header("Content-Type", "text/html")
|
||||
.body(html)
|
||||
.unwrap();
|
||||
let mut res = Response::builder()
|
||||
.header("Content-Type", "text/html")
|
||||
.body(html)
|
||||
.unwrap();
|
||||
|
||||
let headers_mut = res.headers_mut();
|
||||
let headers = server_context.response_parts().unwrap().headers.clone();
|
||||
for (key, value) in headers.iter() {
|
||||
headers_mut.insert(key, value.clone());
|
||||
let headers_mut = res.headers_mut();
|
||||
let headers = server_context.response_parts().unwrap().headers.clone();
|
||||
for (key, value) in headers.iter() {
|
||||
headers_mut.insert(key, value.clone());
|
||||
}
|
||||
freshness.write(headers_mut);
|
||||
|
||||
res
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("Failed to render ssr: {}", err);
|
||||
Response::builder()
|
||||
.status(500)
|
||||
.body("Failed to render ssr".into())
|
||||
.unwrap()
|
||||
}
|
||||
freshness.write(headers_mut);
|
||||
|
||||
res
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("Failed to render ssr: {}", err);
|
||||
Response::builder()
|
||||
.status(500)
|
||||
.body("Failed to render ssr".into())
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// An extractor for the request parts (used in [DioxusServerContext]). This will extract the method, uri, query, and headers from the request.
|
||||
|
@ -273,11 +291,20 @@ pub fn request_parts(
|
|||
})
|
||||
}
|
||||
|
||||
fn with_ssr_state<P: Clone + serde::Serialize + Send + Sync + 'static>(
|
||||
cfg: &ServeConfig<P>,
|
||||
) -> impl Filter<Extract = (SSRState,), Error = std::convert::Infallible> + Clone {
|
||||
fn with_ssr_state(
|
||||
cfg: &ServeConfig,
|
||||
virtual_dom_factory: impl Fn() -> VirtualDom + Send + Sync + 'static,
|
||||
) -> impl Filter<
|
||||
Extract = ((
|
||||
SSRState,
|
||||
Arc<dyn Fn() -> VirtualDom + Send + Sync + 'static>,
|
||||
),),
|
||||
Error = std::convert::Infallible,
|
||||
> + Clone {
|
||||
let renderer = SSRState::new(cfg);
|
||||
warp::any().map(move || renderer.clone())
|
||||
let virtual_dom_factory =
|
||||
Arc::new(virtual_dom_factory) as Arc<dyn Fn() -> VirtualDom + Send + Sync + 'static>;
|
||||
warp::any().map(move || (renderer.clone(), virtual_dom_factory.clone()))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -170,15 +170,19 @@ impl Config {
|
|||
let router = {
|
||||
// Serve the dist folder and the index.html file
|
||||
let serve_dir = warp::fs::dir(cfg.assets_path);
|
||||
let build_virtual_dom = Arc::new(build_virtual_dom);
|
||||
|
||||
router
|
||||
.or(connect_hot_reload())
|
||||
// Then the index route
|
||||
.or(warp::path::end().and(render_ssr(cfg.clone())))
|
||||
.or(warp::path::end().and(render_ssr(cfg.clone(), {
|
||||
let build_virtual_dom = build_virtual_dom.clone();
|
||||
move || build_virtual_dom()
|
||||
})))
|
||||
// Then the static assets
|
||||
.or(serve_dir)
|
||||
// Then all other routes
|
||||
.or(render_ssr(cfg))
|
||||
.or(render_ssr(cfg, move || build_virtual_dom()))
|
||||
};
|
||||
warp::serve(router.boxed().with(warp::filters::compression::gzip()))
|
||||
.run(addr)
|
||||
|
|
Loading…
Reference in a new issue