make server function API more flexible
This commit is contained in:
1 parent
71ddd50963
commit
c6992c7032
9 files changed
+209
-76
No files matched your search
@@ -6,7 +6,7 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
server_fn = { version = "0.2.4", features = ["stable"] }
|
server_fn = { path = "D:/Users/Desktop/github/leptos/server_fn", features = ["stable"] }
|
||||||
server_macro = { path = "server-macro" }
|
server_macro = { path = "server-macro" }
|
||||||
|
|
||||||
# warp
|
# warp
|
||||||
@@ -31,6 +31,7 @@ once_cell = "1.17.1"
|
|||||||
thiserror = "1.0.40"
|
thiserror = "1.0.40"
|
||||||
tokio = { version = "1.27.0", features = ["full"], optional = true }
|
tokio = { version = "1.27.0", features = ["full"], optional = true }
|
||||||
object-pool = "0.5.4"
|
object-pool = "0.5.4"
|
||||||
|
anymap = "0.12.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
quote = "1.0.26"
|
quote = "1.0.26"
|
||||||
server_fn_macro = { version = "0.2.4", features = ["stable"] }
|
# server_fn_macro = { git = "https://github.com/leptos-rs/leptos", rev = "1e037ecb60965c7c55fd781fdc8de7863ffd102b", features = ["stable"] }
|
||||||
|
server_fn_macro = { path = "D:/Users/Desktop/github/leptos/server_fn_macro", features = ["stable"] }
|
||||||
syn = { version = "1", features = ["full"] }
|
syn = { version = "1", features = ["full"] }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use proc_macro::TokenStream;
|
|||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
use server_fn_macro::*;
|
use server_fn_macro::*;
|
||||||
|
|
||||||
/// Declares that a function is a [server function](leptos_server). This means that
|
/// Declares that a function is a [server function](dioxus_server). This means that
|
||||||
/// its body will only run on the server, i.e., when the `ssr` feature is enabled.
|
/// its body will only run on the server, i.e., when the `ssr` feature is enabled.
|
||||||
///
|
///
|
||||||
/// If you call a server function from the client (i.e., when the `csr` or `hydrate` features
|
/// If you call a server function from the client (i.e., when the `csr` or `hydrate` features
|
||||||
@@ -19,18 +19,18 @@ use server_fn_macro::*;
|
|||||||
/// work without WebAssembly, the encoding must be `"Url"`.
|
/// work without WebAssembly, the encoding must be `"Url"`.
|
||||||
///
|
///
|
||||||
/// The server function itself can take any number of arguments, each of which should be serializable
|
/// The server function itself can take any number of arguments, each of which should be serializable
|
||||||
/// and deserializable with `serde`. Optionally, its first argument can be a Leptos [Scope](leptos_reactive::Scope),
|
/// and deserializable with `serde`. Optionally, its first argument can be a [DioxusServerContext](dioxus_server::prelude::DioxusServerContext),
|
||||||
/// which will be injected *on the server side.* This can be used to inject the raw HTTP request or other
|
/// which will be injected *on the server side.* This can be used to inject the raw HTTP request or other
|
||||||
/// server-side context into the server function.
|
/// server-side context into the server function.
|
||||||
///
|
///
|
||||||
/// ```ignore
|
/// ```ignore
|
||||||
/// # use leptos::*; use serde::{Serialize, Deserialize};
|
/// # use dioxus_server::prelude::*; use serde::{Serialize, Deserialize};
|
||||||
/// # #[derive(Serialize, Deserialize)]
|
/// # #[derive(Serialize, Deserialize)]
|
||||||
/// # pub struct Post { }
|
/// # pub struct Post { }
|
||||||
/// #[server(ReadPosts, "/api")]
|
/// #[server(ReadPosts, "/api")]
|
||||||
/// pub async fn read_posts(how_many: u8, query: String) -> Result<Vec<Post>, ServerFnError> {
|
/// pub async fn read_posts(how_many: u8, query: String) -> Result<Vec<Post>, ServerFnError> {
|
||||||
/// // do some work on the server to access the database
|
/// // do some work on the server to access the database
|
||||||
/// todo!()
|
/// todo!()
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
@@ -42,7 +42,7 @@ use server_fn_macro::*;
|
|||||||
/// - **Server functions must return `Result<T, ServerFnError>`.** Even if the work being done
|
/// - **Server functions must return `Result<T, ServerFnError>`.** Even if the work being done
|
||||||
/// inside the function body can’t fail, the processes of serialization/deserialization and the
|
/// inside the function body can’t fail, the processes of serialization/deserialization and the
|
||||||
/// network call are fallible.
|
/// network call are fallible.
|
||||||
/// - **Return types must be [Serializable](leptos_reactive::Serializable).**
|
/// - **Return types must implement [`Serialize`](https://docs.rs/serde/latest/serde/trait.Serialize.html).**
|
||||||
/// This should be fairly obvious: we have to serialize arguments to send them to the server, and we
|
/// This should be fairly obvious: we have to serialize arguments to send them to the server, and we
|
||||||
/// need to deserialize the result to return it to the client.
|
/// need to deserialize the result to return it to the client.
|
||||||
/// - **Arguments must be implement [`Serialize`](https://docs.rs/serde/latest/serde/trait.Serialize.html)
|
/// - **Arguments must be implement [`Serialize`](https://docs.rs/serde/latest/serde/trait.Serialize.html)
|
||||||
@@ -50,8 +50,8 @@ use server_fn_macro::*;
|
|||||||
/// They are serialized as an `application/x-www-form-urlencoded`
|
/// They are serialized as an `application/x-www-form-urlencoded`
|
||||||
/// form data using [`serde_urlencoded`](https://docs.rs/serde_urlencoded/latest/serde_urlencoded/) or as `application/cbor`
|
/// form data using [`serde_urlencoded`](https://docs.rs/serde_urlencoded/latest/serde_urlencoded/) or as `application/cbor`
|
||||||
/// using [`cbor`](https://docs.rs/cbor/latest/cbor/).
|
/// using [`cbor`](https://docs.rs/cbor/latest/cbor/).
|
||||||
/// - **The [Scope](leptos_reactive::Scope) comes from the server.** Optionally, the first argument of a server function
|
/// - **The [DioxusServerContext](dioxus_server::prelude::DioxusServerContext) comes from the server.** Optionally, the first argument of a server function
|
||||||
/// can be a Leptos [Scope](leptos_reactive::Scope). This scope can be used to inject dependencies like the HTTP request
|
/// can be a [DioxusServerContext](dioxus_server::prelude::DioxusServerContext). This scope can be used to inject dependencies like the HTTP request
|
||||||
/// or response or other server-only dependencies, but it does *not* have access to reactive state that exists in the client.
|
/// or response or other server-only dependencies, but it does *not* have access to reactive state that exists in the client.
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
|
pub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ use std::{error::Error, sync::Arc};
|
|||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
body::{self, Body, BoxBody, Full},
|
body::{self, Body, BoxBody, Full},
|
||||||
extract::{FromRef, State},
|
extract::State,
|
||||||
|
handler::Handler,
|
||||||
http::{HeaderMap, Request, Response, StatusCode},
|
http::{HeaderMap, Request, Response, StatusCode},
|
||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
routing::{get, post},
|
routing::{get, post},
|
||||||
@@ -14,61 +15,89 @@ use tokio::task::spawn_blocking;
|
|||||||
use crate::{
|
use crate::{
|
||||||
render::SSRState,
|
render::SSRState,
|
||||||
serve::ServeConfig,
|
serve::ServeConfig,
|
||||||
server_fn::{DioxusServerContext, DioxusServerFnRegistry, ServerFnTraitObj},
|
server_context::DioxusServerContext,
|
||||||
|
server_fn::{DioxusServerFnRegistry, ServerFnTraitObj},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait DioxusRouterExt {
|
pub trait DioxusRouterExt<S> {
|
||||||
|
fn register_server_fns_with_handler<H, T>(
|
||||||
|
self,
|
||||||
|
server_fn_route: &'static str,
|
||||||
|
handler: impl Fn(Arc<ServerFnTraitObj>) -> H,
|
||||||
|
) -> Self
|
||||||
|
where
|
||||||
|
H: Handler<T, S>,
|
||||||
|
T: 'static,
|
||||||
|
S: Clone + Send + Sync + 'static;
|
||||||
fn register_server_fns(self, server_fn_route: &'static str) -> Self;
|
fn register_server_fns(self, server_fn_route: &'static str) -> Self;
|
||||||
|
|
||||||
fn serve_dioxus_application<P: Clone + Send + Sync + 'static>(
|
fn serve_dioxus_application<P: Clone + Send + Sync + 'static>(
|
||||||
self,
|
self,
|
||||||
cfg: ServeConfig<P>,
|
server_fn_route: &'static str,
|
||||||
server_fn_route: Option<&'static str>,
|
cfg: impl Into<ServeConfig<P>>,
|
||||||
) -> Self;
|
) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> DioxusRouterExt for Router<S>
|
impl<S> DioxusRouterExt<S> for Router<S>
|
||||||
where
|
where
|
||||||
SSRState: FromRef<S>,
|
|
||||||
S: Send + Sync + Clone + 'static,
|
S: Send + Sync + Clone + 'static,
|
||||||
{
|
{
|
||||||
fn register_server_fns(self, server_fn_route: &'static str) -> Self {
|
fn register_server_fns_with_handler<H, T>(
|
||||||
|
self,
|
||||||
|
server_fn_route: &'static str,
|
||||||
|
mut handler: impl FnMut(Arc<ServerFnTraitObj>) -> H,
|
||||||
|
) -> Self
|
||||||
|
where
|
||||||
|
H: Handler<T, S, Body>,
|
||||||
|
T: 'static,
|
||||||
|
S: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
let mut router = self;
|
let mut router = self;
|
||||||
for server_fn_path in DioxusServerFnRegistry::paths_registered() {
|
for server_fn_path in DioxusServerFnRegistry::paths_registered() {
|
||||||
let func = DioxusServerFnRegistry::get(server_fn_path).unwrap();
|
let func = DioxusServerFnRegistry::get(server_fn_path).unwrap();
|
||||||
let full_route = format!("{server_fn_route}/{server_fn_path}");
|
let full_route = format!("{server_fn_route}/{server_fn_path}");
|
||||||
router = router.route(
|
router = router.route(&full_route, post(handler(func)));
|
||||||
&full_route,
|
|
||||||
post(move |headers: HeaderMap, body: Request<Body>| async move {
|
|
||||||
server_fn_handler(DioxusServerContext {}, func.clone(), headers, body).await
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
router
|
router
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn register_server_fns(self, server_fn_route: &'static str) -> Self {
|
||||||
|
self.register_server_fns_with_handler(server_fn_route, |func| {
|
||||||
|
move |headers: HeaderMap, body: Request<Body>| async move {
|
||||||
|
server_fn_handler(DioxusServerContext::default(), func.clone(), headers, body).await
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn serve_dioxus_application<P: Clone + Send + Sync + 'static>(
|
fn serve_dioxus_application<P: Clone + Send + Sync + 'static>(
|
||||||
self,
|
self,
|
||||||
cfg: ServeConfig<P>,
|
server_fn_route: &'static str,
|
||||||
server_fn_route: Option<&'static str>,
|
cfg: impl Into<ServeConfig<P>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
use tower_http::services::ServeDir;
|
use tower_http::services::ServeDir;
|
||||||
|
|
||||||
// Serve the dist folder and the index.html file
|
let cfg = cfg.into();
|
||||||
let serve_dir = ServeDir::new("dist");
|
|
||||||
|
|
||||||
self.register_server_fns(server_fn_route.unwrap_or_default())
|
// Serve the dist folder and the index.html file
|
||||||
.route(
|
let serve_dir = ServeDir::new(cfg.assets_path);
|
||||||
|
|
||||||
|
self.register_server_fns(server_fn_route)
|
||||||
|
.nest_service("/assets", serve_dir)
|
||||||
|
.route_service(
|
||||||
"/",
|
"/",
|
||||||
get(move |State(ssr_state): State<SSRState>| {
|
get(render_handler).with_state((cfg, SSRState::default())),
|
||||||
let rendered = ssr_state.render(&cfg);
|
|
||||||
async move { Full::from(rendered) }
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
.fallback_service(serve_dir)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn server_fn_handler(
|
async fn render_handler<P: Clone + Send + Sync + 'static>(
|
||||||
|
State((cfg, ssr_state)): State<(ServeConfig<P>, SSRState)>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
let rendered = ssr_state.render(&cfg);
|
||||||
|
Full::from(rendered)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn server_fn_handler(
|
||||||
server_context: DioxusServerContext,
|
server_context: DioxusServerContext,
|
||||||
function: Arc<ServerFnTraitObj>,
|
function: Arc<ServerFnTraitObj>,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
|
|||||||
@@ -9,43 +9,65 @@ use server_fn::{Payload, ServerFunctionRegistry};
|
|||||||
use tokio::task::spawn_blocking;
|
use tokio::task::spawn_blocking;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dioxus_ssr_html,
|
prelude::DioxusServerContext,
|
||||||
|
prelude::SSRState,
|
||||||
serve::ServeConfig,
|
serve::ServeConfig,
|
||||||
server_fn::{DioxusServerContext, DioxusServerFnRegistry, ServerFnTraitObj},
|
server_fn::{DioxusServerFnRegistry, ServerFnTraitObj},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait DioxusRouterExt {
|
pub trait DioxusRouterExt {
|
||||||
fn register_server_fns(self, server_fn_route: &'static str) -> Self;
|
fn register_server_fns(self, server_fn_route: &'static str) -> Self;
|
||||||
|
fn register_server_fns_with_handler<H>(
|
||||||
|
self,
|
||||||
|
server_fn_route: &'static str,
|
||||||
|
handler: impl Fn(Arc<ServerFnTraitObj>) -> H,
|
||||||
|
) -> Self
|
||||||
|
where
|
||||||
|
H: Handler + 'static;
|
||||||
fn serve_dioxus_application<P: Clone + Send + Sync + 'static>(
|
fn serve_dioxus_application<P: Clone + Send + Sync + 'static>(
|
||||||
self,
|
self,
|
||||||
cfg: ServeConfig<P>,
|
server_fn_path: &'static str,
|
||||||
|
cfg: impl Into<ServeConfig<P>>,
|
||||||
) -> Self;
|
) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DioxusRouterExt for Router {
|
impl DioxusRouterExt for Router {
|
||||||
fn register_server_fns(self, server_fn_route: &'static str) -> Self {
|
fn register_server_fns_with_handler<H>(
|
||||||
|
self,
|
||||||
|
server_fn_route: &'static str,
|
||||||
|
mut handler: impl FnMut(Arc<ServerFnTraitObj>) -> H,
|
||||||
|
) -> Self
|
||||||
|
where
|
||||||
|
H: Handler + 'static,
|
||||||
|
{
|
||||||
let mut router = self;
|
let mut router = self;
|
||||||
for server_fn_path in DioxusServerFnRegistry::paths_registered() {
|
for server_fn_path in DioxusServerFnRegistry::paths_registered() {
|
||||||
let func = DioxusServerFnRegistry::get(server_fn_path).unwrap();
|
let func = DioxusServerFnRegistry::get(server_fn_path).unwrap();
|
||||||
let full_route = format!("{server_fn_route}/{server_fn_path}");
|
let full_route = format!("{server_fn_route}/{server_fn_path}");
|
||||||
router = router.push(Router::with_path(&full_route).post(ServerFnHandler {
|
router = router.push(Router::with_path(&full_route).post(handler(func)));
|
||||||
server_context: DioxusServerContext {},
|
|
||||||
function: func,
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
router
|
router
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn register_server_fns(self, server_fn_route: &'static str) -> Self {
|
||||||
|
self.register_server_fns_with_handler(|| ServerFnHandler {
|
||||||
|
server_context: DioxusServerContext::default(),
|
||||||
|
function: func,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn serve_dioxus_application<P: Clone + Send + Sync + 'static>(
|
fn serve_dioxus_application<P: Clone + Send + Sync + 'static>(
|
||||||
self,
|
self,
|
||||||
cfg: ServeConfig<P>,
|
server_fn_route: &'static str,
|
||||||
|
cfg: impl Into<ServeConfig<P>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let cfg = cfg.into();
|
||||||
// Serve the dist folder and the index.html file
|
// Serve the dist folder and the index.html file
|
||||||
let serve_dir = StaticDir::new(["dist"]);
|
let serve_dir = StaticDir::new([cfg.assets_path]);
|
||||||
|
|
||||||
self.register_server_fns(cfg.server_fn_route.unwrap_or_default())
|
self.register_server_fns(server_fn_route)
|
||||||
.push(Router::with_path("/").get(SSRHandler { cfg }))
|
.push(Router::with_path("/").get(SSRHandler { cfg }))
|
||||||
.push(Router::with_path("<**path>").get(serve_dir))
|
.push(Router::with_path("assets/<**path>").get(serve_dir))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,19 +80,36 @@ impl<P: Clone + Send + Sync + 'static> Handler for SSRHandler<P> {
|
|||||||
async fn handle(
|
async fn handle(
|
||||||
&self,
|
&self,
|
||||||
_req: &mut Request,
|
_req: &mut Request,
|
||||||
_depot: &mut Depot,
|
depot: &mut Depot,
|
||||||
res: &mut Response,
|
res: &mut Response,
|
||||||
_flow: &mut FlowCtrl,
|
_flow: &mut FlowCtrl,
|
||||||
) {
|
) {
|
||||||
res.write_body(dioxus_ssr_html(&self.cfg)).unwrap();
|
// Get the SSR renderer from the depot or create a new one if it doesn't exist
|
||||||
|
let renderer_pool = if let Some(renderer) = depot.obtain::<SSRState>() {
|
||||||
|
renderer.clone()
|
||||||
|
} else {
|
||||||
|
let renderer = SSRState::default();
|
||||||
|
depot.inject(renderer.clone());
|
||||||
|
renderer
|
||||||
|
};
|
||||||
|
res.write_body(renderer_pool.render(&self.cfg)).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ServerFnHandler {
|
pub struct ServerFnHandler {
|
||||||
server_context: DioxusServerContext,
|
server_context: DioxusServerContext,
|
||||||
function: Arc<ServerFnTraitObj>,
|
function: Arc<ServerFnTraitObj>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ServerFnHandler {
|
||||||
|
pub fn new(server_context: DioxusServerContext, function: Arc<ServerFnTraitObj>) -> Self {
|
||||||
|
Self {
|
||||||
|
server_context,
|
||||||
|
function,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[handler]
|
#[handler]
|
||||||
impl ServerFnHandler {
|
impl ServerFnHandler {
|
||||||
async fn handle(&self, req: &mut Request, _depot: &mut Depot, res: &mut Response) {
|
async fn handle(&self, req: &mut Request, _depot: &mut Depot, res: &mut Response) {
|
||||||
|
|||||||
@@ -10,50 +10,75 @@ use warp::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dioxus_ssr_html,
|
prelude::{DioxusServerContext, SSRState},
|
||||||
serve::ServeConfig,
|
serve::ServeConfig,
|
||||||
server_fn::{DioxusServerContext, DioxusServerFnRegistry, ServerFnTraitObj},
|
server_fn::{DioxusServerFnRegistry, ServerFnTraitObj},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn register_server_fns(server_fn_route: &'static str) -> BoxedFilter<(impl Reply,)> {
|
pub fn register_server_fns_with_handler<H, F, R>(
|
||||||
let mut filter: Option<BoxedFilter<(_,)>> = None;
|
server_fn_route: &'static str,
|
||||||
|
mut handler: H,
|
||||||
|
) -> BoxedFilter<(R,)>
|
||||||
|
where
|
||||||
|
H: FnMut(String, Arc<ServerFnTraitObj>) -> F,
|
||||||
|
F: Filter<Extract = (R,), Error = warp::Rejection> + Send + Sync + 'static,
|
||||||
|
F::Extract: Send,
|
||||||
|
R: Reply + 'static,
|
||||||
|
{
|
||||||
|
let mut filter: Option<BoxedFilter<F::Extract>> = None;
|
||||||
for server_fn_path in DioxusServerFnRegistry::paths_registered() {
|
for server_fn_path in DioxusServerFnRegistry::paths_registered() {
|
||||||
let func = DioxusServerFnRegistry::get(server_fn_path).unwrap();
|
let func = DioxusServerFnRegistry::get(server_fn_path).unwrap();
|
||||||
let full_route = format!("{server_fn_route}/{server_fn_path}")
|
let full_route = format!("{server_fn_route}/{server_fn_path}")
|
||||||
.trim_start_matches('/')
|
.trim_start_matches('/')
|
||||||
.to_string();
|
.to_string();
|
||||||
let route = path(full_route)
|
let route = handler(full_route, func.clone()).boxed();
|
||||||
.and(warp::post())
|
|
||||||
.and(warp::header::headers_cloned())
|
|
||||||
.and(warp::body::bytes())
|
|
||||||
.and_then(move |headers: HeaderMap, body| {
|
|
||||||
let func = func.clone();
|
|
||||||
async move { server_fn_handler(DioxusServerContext {}, func, headers, body).await }
|
|
||||||
})
|
|
||||||
.boxed();
|
|
||||||
if let Some(boxed_filter) = filter.take() {
|
if let Some(boxed_filter) = filter.take() {
|
||||||
filter = Some(boxed_filter.or(route).unify().boxed());
|
filter = Some(boxed_filter.or(route).unify().boxed());
|
||||||
} else {
|
} else {
|
||||||
filter = Some(route.boxed());
|
filter = Some(route);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filter.expect("No server functions found")
|
filter.expect("No server functions found")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve_dioxus_application<P: Clone + Send + Sync + 'static>(
|
pub fn register_server_fns(server_fn_route: &'static str) -> BoxedFilter<(impl Reply,)> {
|
||||||
cfg: ServeConfig<P>,
|
register_server_fns_with_handler(server_fn_route, |full_route, func| {
|
||||||
) -> BoxedFilter<(impl Reply,)> {
|
path(full_route)
|
||||||
// Serve the dist folder and the index.html file
|
.and(warp::post())
|
||||||
let serve_dir = warp::fs::dir("./dist");
|
.and(warp::header::headers_cloned())
|
||||||
|
.and(warp::body::bytes())
|
||||||
|
.and_then(move |headers: HeaderMap, body| {
|
||||||
|
let func = func.clone();
|
||||||
|
async move {
|
||||||
|
server_fn_handler(DioxusServerContext::default(), func, headers, body).await
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
register_server_fns(cfg.server_fn_route.unwrap_or_default())
|
pub fn serve_dioxus_application<P: Clone + Send + Sync + 'static>(
|
||||||
|
server_fn_route: &'static str,
|
||||||
|
cfg: impl Into<ServeConfig<P>>,
|
||||||
|
) -> 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);
|
||||||
|
|
||||||
|
register_server_fns(server_fn_route)
|
||||||
.or(warp::path::end()
|
.or(warp::path::end()
|
||||||
.and(warp::get())
|
.and(warp::get())
|
||||||
.map(move || warp::reply::html(dioxus_ssr_html(&cfg))))
|
.and(with_ssr_state())
|
||||||
.or(serve_dir)
|
.map(move |renderer: SSRState| warp::reply::html(renderer.render(&cfg))))
|
||||||
|
.or(warp::path("assets").and(serve_dir))
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn with_ssr_state() -> impl Filter<Extract = (SSRState,), Error = std::convert::Infallible> + Clone
|
||||||
|
{
|
||||||
|
let renderer = SSRState::default();
|
||||||
|
warp::any().map(move || renderer.clone())
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct FailedToReadBody(String);
|
struct FailedToReadBody(String);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ mod adapters;
|
|||||||
pub mod render;
|
pub mod render;
|
||||||
#[cfg(feature = "ssr")]
|
#[cfg(feature = "ssr")]
|
||||||
mod serve;
|
mod serve;
|
||||||
|
mod server_context;
|
||||||
mod server_fn;
|
mod server_fn;
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
@@ -18,8 +19,11 @@ pub mod prelude {
|
|||||||
#[cfg(feature = "ssr")]
|
#[cfg(feature = "ssr")]
|
||||||
pub use crate::render::*;
|
pub use crate::render::*;
|
||||||
#[cfg(feature = "ssr")]
|
#[cfg(feature = "ssr")]
|
||||||
pub use crate::serve::ServeConfig;
|
pub use crate::serve::{ServeConfig, ServeConfigBuilder};
|
||||||
pub use crate::server_fn::{DioxusServerContext, ServerFn};
|
pub use crate::server_context::DioxusServerContext;
|
||||||
|
pub use crate::server_fn::ServerFn;
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
pub use crate::server_fn::ServerFnTraitObj;
|
||||||
pub use server_fn::{self, ServerFn as _, ServerFnError};
|
pub use server_fn::{self, ServerFn as _, ServerFnError};
|
||||||
pub use server_macro::*;
|
pub use server_macro::*;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
use std::sync::{Arc, PoisonError, RwLock, RwLockWriteGuard};
|
||||||
|
|
||||||
|
use anymap::{any::Any, Map};
|
||||||
|
|
||||||
|
type SendSyncAnyMap = Map<dyn Any + Send + Sync + 'static>;
|
||||||
|
|
||||||
|
/// A shared context for server functions. This allows you to pass data between your server and the server functions like authentication session data.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct DioxusServerContext {
|
||||||
|
shared_context: Arc<RwLock<SendSyncAnyMap>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DioxusServerContext {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
shared_context: Arc::new(RwLock::new(SendSyncAnyMap::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DioxusServerContext {
|
||||||
|
pub fn get<T: Any + Send + Sync + Clone + 'static>(&self) -> Option<T> {
|
||||||
|
self.shared_context.read().ok()?.get::<T>().cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert<T: Any + Send + Sync + 'static>(
|
||||||
|
&mut self,
|
||||||
|
value: T,
|
||||||
|
) -> Result<(), PoisonError<RwLockWriteGuard<'_, SendSyncAnyMap>>> {
|
||||||
|
self.shared_context
|
||||||
|
.write()
|
||||||
|
.map(|mut map| map.insert(value))
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
#[derive(Clone)]
|
use crate::server_context::DioxusServerContext;
|
||||||
pub struct DioxusServerContext {}
|
|
||||||
|
|
||||||
#[cfg(any(feature = "ssr", doc))]
|
#[cfg(any(feature = "ssr", doc))]
|
||||||
pub type ServerFnTraitObj = server_fn::ServerFnTraitObj<DioxusServerContext>;
|
pub type ServerFnTraitObj = server_fn::ServerFnTraitObj<DioxusServerContext>;
|
||||||
|
|||||||
Reference in new issue
Block a user