fix other fullstack adapters

This commit is contained in:
Evan Almloff 2024-01-18 11:39:51 -06:00
parent 694bef0d93
commit 89b1e56fc3
9 changed files with 105 additions and 95 deletions

5
Cargo.lock generated
View file

@ -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]]

View file

@ -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!" }

View file

@ -1,8 +1,7 @@
//! Run with:
//!
//! ```sh
//! dx build --features web --release
//! cargo run --features ssr --release
//! dx serve --platform fullstack
//! ```
use dioxus::prelude::*;

View file

@ -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)]

View file

@ -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"]

View file

@ -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);
}

View file

@ -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,

View file

@ -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)]

View file

@ -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)