add extractors to server-fn

This commit is contained in:
Evan Almloff 2023-07-06 17:54:05 -07:00
parent 523be82965
commit 4f6e6a7c0d
12 changed files with 260 additions and 139 deletions

View file

@ -13,7 +13,7 @@ keywords = ["dom", "ui", "gui", "react", "ssr", "fullstack"]
[dependencies]
# server functions
server_fn = { git = "https://github.com/leptos-rs/leptos", rev = "15a4e54435eb5a539afb75891292bcccd2cc8e85", default-features = false, features = ["stable"] }
server_fn = { git = "https://github.com/leptos-rs/leptos", rev = "6b90e1babd425c9a324181c86e3fd1b942c9b10f", default-features = false }
dioxus_server_macro = { path = "server-macro" }
# warp
@ -53,6 +53,9 @@ postcard = { version = "1.0.4", features = ["use-std"] }
yazi = "0.1.5"
base64 = "0.21.0"
pin-project = "1.1.2"
async-trait = "0.1.71"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
dioxus-hot-reload = { workspace = true }

View file

@ -26,12 +26,11 @@ fn app(cx: Scope<AppProps>) -> Element {
button {
onclick: move |_| {
to_owned![text];
let sc = cx.sc();
async move {
if let Ok(data) = get_server_data().await {
println!("Client received: {}", data);
text.set(data.clone());
post_server_data(sc, data).await.unwrap();
post_server_data(data).await.unwrap();
}
}
},
@ -42,8 +41,9 @@ fn app(cx: Scope<AppProps>) -> Element {
}
#[server(PostServerData)]
async fn post_server_data(cx: DioxusServerContext, data: String) -> Result<(), ServerFnError> {
async fn post_server_data(data: String) -> Result<(), ServerFnError> {
// The server context contains information about the current request and allows you to modify the response.
let cx = server_context();
cx.response_headers_mut()
.insert("Set-Cookie", "foo=bar".parse().unwrap());
println!("Server received: {}", data);

View file

@ -7,7 +7,7 @@ edition = "2021"
[dependencies]
quote = "1.0.26"
server_fn_macro = { git = "https://github.com/leptos-rs/leptos", rev = "15a4e54435eb5a539afb75891292bcccd2cc8e85", features = ["stable"] }
server_fn_macro = { git = "https://github.com/leptos-rs/leptos", rev = "6b90e1babd425c9a324181c86e3fd1b942c9b10f" }
syn = { version = "2", features = ["full"] }
[lib]

View file

@ -54,15 +54,11 @@ use server_fn_macro::*;
/// or response or other server-only dependencies, but it does *not* have access to reactive state that exists in the client.
#[proc_macro_attribute]
pub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
let context = ServerContext {
ty: syn::parse_quote!(DioxusServerContext),
path: syn::parse_quote!(::dioxus_fullstack::prelude::DioxusServerContext),
};
match server_macro_impl(
args.into(),
s.into(),
syn::parse_quote!(::dioxus_fullstack::prelude::ServerFnTraitObj),
Some(context),
None,
Some(syn::parse_quote!(::dioxus_fullstack::prelude::server_fn)),
) {
Err(e) => e.to_compile_error().into(),

View file

@ -64,8 +64,8 @@ use axum::{
Router,
};
use server_fn::{Encoding, Payload, ServerFunctionRegistry};
use std::error::Error;
use std::sync::Arc;
use std::sync::RwLock;
use tokio::task::spawn_blocking;
use crate::{
@ -91,7 +91,7 @@ pub trait DioxusRouterExt<S> {
/// .register_server_fns_with_handler("", |func| {
/// move |req: Request<Body>| async move {
/// let (parts, body) = req.into_parts();
/// let parts: Arc<RequestParts> = Arc::new(parts.into());
/// let parts: Arc<http::request::Parts> = Arc::new(parts.into());
/// let server_context = DioxusServerContext::new(parts.clone());
/// server_fn_handler(server_context, func.clone(), parts, body).await
/// }
@ -105,7 +105,7 @@ pub trait DioxusRouterExt<S> {
fn register_server_fns_with_handler<H, T>(
self,
server_fn_route: &'static str,
handler: impl FnMut(server_fn::ServerFnTraitObj<DioxusServerContext>) -> H,
handler: impl FnMut(server_fn::ServerFnTraitObj<()>) -> H,
) -> Self
where
H: Handler<T, S>,
@ -230,7 +230,7 @@ where
fn register_server_fns_with_handler<H, T>(
self,
server_fn_route: &'static str,
mut handler: impl FnMut(server_fn::ServerFnTraitObj<DioxusServerContext>) -> H,
mut handler: impl FnMut(server_fn::ServerFnTraitObj<()>) -> H,
) -> Self
where
H: Handler<T, S, Body>,
@ -257,7 +257,7 @@ where
self.register_server_fns_with_handler(server_fn_route, |func| {
move |req: Request<Body>| async move {
let (parts, body) = req.into_parts();
let parts: Arc<RequestParts> = Arc::new(parts.into());
let parts: Arc<RwLock<http::request::Parts>> = Arc::new(parts.into());
let server_context = DioxusServerContext::new(parts.clone());
server_fn_handler(server_context, func.clone(), parts, body).await
}
@ -364,16 +364,11 @@ async fn render_handler<P: Clone + serde::Serialize + Send + Sync + 'static>(
request: Request<Body>,
) -> impl IntoResponse {
let (parts, _) = request.into_parts();
let parts: Arc<RequestParts> = Arc::new(parts.into());
let url = parts.uri.path_and_query().unwrap().to_string();
let parts: Arc<RwLock<http::request::Parts>> = Arc::new(RwLock::new(parts.into()));
let server_context = DioxusServerContext::new(parts.clone());
match ssr_state
.render(url, &cfg, |vdom| {
vdom.base_scope().provide_context(server_context.clone());
})
.await
{
match ssr_state.render(url, &cfg, &server_context).await {
Ok(rendered) => {
let crate::render::RenderResponse { html, freshness } = rendered;
let mut response = axum::response::Html::from(html).into_response();
@ -392,8 +387,8 @@ async fn render_handler<P: Clone + serde::Serialize + Send + Sync + 'static>(
/// A default handler for server functions. It will deserialize the request, call the server function, and serialize the response.
pub async fn server_fn_handler(
server_context: DioxusServerContext,
function: server_fn::ServerFnTraitObj<DioxusServerContext>,
parts: Arc<RequestParts>,
function: server_fn::ServerFnTraitObj<()>,
parts: Arc<RwLock<http::request::Parts>>,
body: Body,
) -> impl IntoResponse {
let body = hyper::body::to_bytes(body).await;
@ -403,7 +398,13 @@ pub async fn server_fn_handler(
// Because the future returned by `server_fn_handler` is `Send`, and the future returned by this function must be send, we need to spawn a new runtime
let (resp_tx, resp_rx) = tokio::sync::oneshot::channel();
let query_string = parts.uri.query().unwrap_or_default().to_string();
let query_string = parts
.read()
.unwrap()
.uri
.query()
.unwrap_or_default()
.to_string();
spawn_blocking({
move || {
tokio::runtime::Runtime::new()
@ -414,9 +415,13 @@ pub async fn server_fn_handler(
Encoding::Url | Encoding::Cbor => &body,
Encoding::GetJSON | Encoding::GetCBOR => query,
};
let resp = match function.call(server_context.clone(), data).await {
let server_function_future = function.call((), data);
let server_function_future =
ProvideServerContext::new(server_function_future, server_context.clone());
let resp = match server_function_future.await {
Ok(serialized) => {
// if this is Accept: application/json then send a serialized JSON response
let parts = parts.read().unwrap();
let accept_header = parts
.headers
.get("Accept")
@ -463,7 +468,7 @@ pub async fn server_fn_handler(
resp_rx.await.unwrap()
}
fn report_err<E: Error>(e: E) -> Response<BoxBody> {
fn report_err<E: std::fmt::Display>(e: E) -> Response<BoxBody> {
Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(body::boxed(format!("Error: {}", e)))

View file

@ -75,7 +75,7 @@ pub trait DioxusRouterExt {
/// use dioxus_fullstack::prelude::*;
///
/// struct ServerFunctionHandler {
/// server_fn: server_fn::ServerFnTraitObj<DioxusServerContext>,
/// server_fn: server_fn::ServerFnTraitObj<()>,
/// }
///
/// #[handler]
@ -108,7 +108,7 @@ pub trait DioxusRouterExt {
fn register_server_fns_with_handler<H>(
self,
server_fn_route: &'static str,
handler: impl Fn(server_fn::ServerFnTraitObj<DioxusServerContext>) -> H,
handler: impl Fn(server_fn::ServerFnTraitObj<()>) -> H,
) -> Self
where
H: Handler + 'static;
@ -202,7 +202,7 @@ impl DioxusRouterExt for Router {
fn register_server_fns_with_handler<H>(
self,
server_fn_route: &'static str,
mut handler: impl FnMut(server_fn::ServerFnTraitObj<DioxusServerContext>) -> H,
mut handler: impl FnMut(server_fn::ServerFnTraitObj<()>) -> H,
) -> Self
where
H: Handler + 'static,
@ -333,9 +333,7 @@ impl<P: Clone + serde::Serialize + Send + Sync + 'static> Handler for SSRHandler
let server_context = DioxusServerContext::new(parts);
match renderer_pool
.render(route, &self.cfg, |vdom| {
vdom.base_scope().provide_context(server_context.clone());
})
.render(route, &self.cfg, &server_context)
.await
{
Ok(rendered) => {
@ -357,14 +355,14 @@ impl<P: Clone + serde::Serialize + Send + Sync + 'static> Handler for SSRHandler
/// A default handler for server functions. It will deserialize the request body, call the server function, and serialize the response.
pub struct ServerFnHandler {
server_context: DioxusServerContext,
function: server_fn::ServerFnTraitObj<DioxusServerContext>,
function: server_fn::ServerFnTraitObj<()>,
}
impl ServerFnHandler {
/// Create a new server function handler with the given server context and server function.
pub fn new(
server_context: impl Into<DioxusServerContext>,
function: server_fn::ServerFnTraitObj<DioxusServerContext>,
function: server_fn::ServerFnTraitObj<()>,
) -> Self {
let server_context = server_context.into();
Self {
@ -413,7 +411,12 @@ impl ServerFnHandler {
Encoding::Url | Encoding::Cbor => &body,
Encoding::GetJSON | Encoding::GetCBOR => &query,
};
let resp = function.call(server_context, data).await;
let server_function_future = function.call((), data);
let server_function_future = ProvideServerContext::new(
server_function_future,
server_context.clone(),
);
let resp = server_function_future.await;
resp_tx.send(resp).unwrap();
})

View file

@ -91,7 +91,7 @@ pub fn register_server_fns_with_handler<H, F, R>(
mut handler: H,
) -> BoxedFilter<(R,)>
where
H: FnMut(String, server_fn::ServerFnTraitObj<DioxusServerContext>) -> F,
H: FnMut(String, server_fn::ServerFnTraitObj<()>) -> F,
F: Filter<Extract = (R,), Error = warp::Rejection> + Send + Sync + 'static,
F::Extract: Send,
R: Reply + 'static,
@ -192,12 +192,7 @@ pub fn render_ssr<P: Clone + serde::Serialize + Send + Sync + 'static>(
async move {
let server_context = DioxusServerContext::new(parts);
match renderer
.render(route, &cfg, |vdom| {
vdom.base_scope().provide_context(server_context.clone());
})
.await
{
match renderer.render(route, &cfg, &server_context).await {
Ok(rendered) => {
let crate::render::RenderResponse { html, freshness } = rendered;
@ -270,8 +265,8 @@ impl warp::reject::Reject for RecieveFailed {}
/// A default handler for server functions. It will deserialize the request body, call the server function, and serialize the response.
pub async fn server_fn_handler(
server_context: impl Into<DioxusServerContext>,
function: server_fn::ServerFnTraitObj<DioxusServerContext>,
server_context: impl Into<(DioxusServerContext)>,
function: server_fn::ServerFnTraitObj<()>,
parts: RequestParts,
body: Bytes,
) -> Result<Box<dyn warp::Reply>, warp::Rejection> {
@ -299,7 +294,10 @@ pub async fn server_fn_handler(
Encoding::Url | Encoding::Cbor => &body,
Encoding::GetJSON | Encoding::GetCBOR => &query,
};
let resp = match function.call(server_context.clone(), data).await {
let server_function_future = function.call((), data);
let server_function_future =
ProvideServerContext::new(server_function_future, server_context.clone());
let resp = match server_function_future.await {
Ok(serialized) => {
// if this is Accept: application/json then send a serialized JSON response
let accept_header = parts

View file

@ -18,6 +18,7 @@ pub mod launch;
mod render;
#[cfg(feature = "ssr")]
mod serve_config;
#[cfg(feature = "ssr")]
mod server_context;
mod server_fn;
@ -38,8 +39,11 @@ pub mod prelude {
#[cfg(feature = "ssr")]
pub use crate::serve_config::{ServeConfig, ServeConfigBuilder};
#[cfg(feature = "ssr")]
pub use crate::server_context::RequestParts;
pub use crate::server_context::{DioxusServerContext, HasServerContext};
pub use crate::server_context::Axum;
#[cfg(feature = "ssr")]
pub use crate::server_context::{
server_context, DioxusServerContext, FromServerContext, ProvideServerContext,
};
pub use crate::server_fn::DioxusServerFn;
#[cfg(feature = "ssr")]
pub use crate::server_fn::{ServerFnTraitObj, ServerFunction};

View file

@ -9,7 +9,7 @@ use dioxus_ssr::{
};
use serde::Serialize;
use crate::prelude::*;
use crate::{prelude::*, server_context::with_server_context};
use dioxus::prelude::*;
enum SsrRendererPool {
@ -25,15 +25,17 @@ impl SsrRendererPool {
component: Component<P>,
props: P,
to: &mut String,
modify_vdom: impl FnOnce(&mut VirtualDom),
server_context: &DioxusServerContext,
) -> Result<RenderFreshness, dioxus_ssr::incremental::IncrementalRendererError> {
let wrapper = FullstackRenderer { cfg };
match self {
Self::Renderer(pool) => {
let server_context = Box::new(server_context.clone());
let mut vdom = VirtualDom::new_with_props(component, props);
modify_vdom(&mut vdom);
with_server_context(server_context, || {
let _ = vdom.rebuild();
});
let mut renderer = pool.pull(pre_renderer);
@ -48,7 +50,19 @@ impl SsrRendererPool {
let mut renderer =
pool.pull(|| incremental_pre_renderer(cfg.incremental.as_ref().unwrap()));
Ok(renderer
.render_to_string(route, component, props, to, modify_vdom, &wrapper)
.render_to_string(
route,
component,
props,
to,
|vdom| {
let server_context = Box::new(server_context.clone());
with_server_context(server_context, || {
let _ = vdom.rebuild();
});
},
&wrapper,
)
.await?)
}
}
@ -86,7 +100,7 @@ impl SSRState {
&'a self,
route: String,
cfg: &'a ServeConfig<P>,
modify_vdom: impl FnOnce(&mut VirtualDom) + Send + 'a,
server_context: &'a DioxusServerContext,
) -> impl std::future::Future<
Output = Result<RenderResponse, dioxus_ssr::incremental::IncrementalRendererError>,
> + Send
@ -97,7 +111,7 @@ impl SSRState {
let freshness = self
.renderers
.render_to(cfg, route, *app, props.clone(), &mut html, modify_vdom)
.render_to(cfg, route, *app, props.clone(), &mut html, server_context)
.await?;
Ok(RenderResponse { html, freshness })

View file

@ -1,28 +1,6 @@
use dioxus::prelude::ScopeState;
/// A trait for an object that contains a server context
pub trait HasServerContext {
/// Get the server context from the state
fn server_context(&self) -> DioxusServerContext;
/// A shortcut for `self.server_context()`
fn sc(&self) -> DioxusServerContext {
self.server_context()
}
}
impl HasServerContext for &ScopeState {
fn server_context(&self) -> DioxusServerContext {
#[cfg(feature = "ssr")]
{
self.consume_context().expect("No server context found")
}
#[cfg(not(feature = "ssr"))]
{
DioxusServerContext {}
}
}
}
pub use server_fn_impl::*;
use std::sync::Arc;
use std::sync::RwLock;
/// A shared context for server functions that contains infomation about the request and middleware state.
/// This allows you to pass data between your server framework and the server functions. This can be used to pass request information or information about the state of the server. For example, you could pass authentication data though this context to your server functions.
@ -30,34 +8,24 @@ impl HasServerContext for &ScopeState {
/// You should not construct this directly inside components. Instead use the `HasServerContext` trait to get the server context from the scope.
#[derive(Clone)]
pub struct DioxusServerContext {
#[cfg(feature = "ssr")]
shared_context: std::sync::Arc<
std::sync::RwLock<anymap::Map<dyn anymap::any::Any + Send + Sync + 'static>>,
>,
#[cfg(feature = "ssr")]
headers: std::sync::Arc<std::sync::RwLock<hyper::header::HeaderMap>>,
#[cfg(feature = "ssr")]
pub(crate) parts: std::sync::Arc<RequestParts>,
pub(crate) parts: Arc<RwLock<http::request::Parts>>,
}
#[allow(clippy::derivable_impls)]
impl Default for DioxusServerContext {
fn default() -> Self {
Self {
#[cfg(feature = "ssr")]
shared_context: std::sync::Arc::new(std::sync::RwLock::new(anymap::Map::new())),
#[cfg(feature = "ssr")]
headers: Default::default(),
#[cfg(feature = "ssr")]
parts: Default::default(),
parts: std::sync::Arc::new(RwLock::new(http::request::Request::new(()).into_parts().0)),
}
}
}
#[cfg(feature = "ssr")]
pub use server_fn_impl::*;
#[cfg(feature = "ssr")]
mod server_fn_impl {
use super::*;
use std::sync::LockResult;
@ -68,7 +36,7 @@ mod server_fn_impl {
impl DioxusServerContext {
/// Create a new server context from a request
pub fn new(parts: impl Into<Arc<RequestParts>>) -> Self {
pub fn new(parts: impl Into<Arc<RwLock<http::request::Parts>>>) -> Self {
Self {
parts: parts.into(),
shared_context: Arc::new(RwLock::new(SendSyncAnyMap::new())),
@ -126,35 +94,170 @@ mod server_fn_impl {
/// Get the request that triggered:
/// - The initial SSR render if called from a ScopeState or ServerFn
/// - The server function to be called if called from a server function after the initial render
pub fn request_parts(&self) -> &RequestParts {
&self.parts
}
pub fn request_parts(
&self,
) -> std::sync::LockResult<RwLockReadGuard<'_, http::request::Parts>> {
self.parts.read()
}
/// Associated parts of an HTTP Request
#[derive(Debug, Default)]
pub struct RequestParts {
/// The request's method
pub method: http::Method,
/// The request's URI
pub uri: http::Uri,
/// The request's version
pub version: http::Version,
/// The request's headers
pub headers: http::HeaderMap<http::HeaderValue>,
/// The request's extensions
pub extensions: http::Extensions,
/// Get the request that triggered:
/// - The initial SSR render if called from a ScopeState or ServerFn
/// - The server function to be called if called from a server function after the initial render
pub fn request_parts_mut(
&self,
) -> std::sync::LockResult<RwLockWriteGuard<'_, http::request::Parts>> {
self.parts.write()
}
impl From<http::request::Parts> for RequestParts {
fn from(parts: http::request::Parts) -> Self {
Self {
method: parts.method,
uri: parts.uri,
version: parts.version,
headers: parts.headers,
extensions: parts.extensions,
}
/// Extract some part from the request
pub async fn extract<R: std::error::Error, T: FromServerContext<Rejection = R>>(
&self,
) -> Result<T, R> {
T::from_request(self).await
}
}
}
std::thread_local! {
static SERVER_CONTEXT: std::cell::RefCell<Box<DioxusServerContext>> = std::cell::RefCell::new(Box::new(DioxusServerContext::default() ));
}
/// Get information about the current server request.
///
/// This function will only provide the current server context if it is called from a server function.
pub fn server_context() -> DioxusServerContext {
SERVER_CONTEXT.with(|ctx| *ctx.borrow_mut().clone())
}
pub(crate) fn with_server_context<O>(
context: Box<DioxusServerContext>,
f: impl FnOnce() -> O,
) -> (O, Box<DioxusServerContext>) {
// before polling the future, we need to set the context
let prev_context = SERVER_CONTEXT.with(|ctx| ctx.replace(context));
// poll the future, which may call server_context()
let result = f();
// after polling the future, we need to restore the context
(result, SERVER_CONTEXT.with(|ctx| ctx.replace(prev_context)))
}
/// A future that provides the server context to the inner future
#[pin_project::pin_project]
pub struct ProvideServerContext<F: std::future::Future> {
context: Option<Box<DioxusServerContext>>,
#[pin]
f: F,
}
impl<F: std::future::Future> ProvideServerContext<F> {
/// Create a new future that provides the server context to the inner future
pub fn new(f: F, context: DioxusServerContext) -> Self {
Self {
context: Some(Box::new(context)),
f,
}
}
}
impl<F: std::future::Future> std::future::Future for ProvideServerContext<F> {
type Output = F::Output;
fn poll(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
let this = self.project();
let context = this.context.take().unwrap();
let (result, context) = with_server_context(context, || this.f.poll(cx));
*this.context = Some(context);
result
}
}
/// A trait for extracting types from the server context
#[async_trait::async_trait(?Send)]
pub trait FromServerContext: Sized {
/// The error type returned when extraction fails. This type must implement `IntoResponse`.
type Rejection: std::error::Error;
/// Extract this type from the server context.
async fn from_request(req: &DioxusServerContext) -> Result<Self, Self::Rejection>;
}
/// A type was not found in the server context
pub struct NotFoundInServerContext<T: 'static>(std::marker::PhantomData<T>);
impl<T: 'static> std::fmt::Debug for NotFoundInServerContext<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let type_name = std::any::type_name::<T>();
write!(f, "`{type_name}` not found in server context")
}
}
impl<T: 'static> std::fmt::Display for NotFoundInServerContext<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let type_name = std::any::type_name::<T>();
write!(f, "`{type_name}` not found in server context")
}
}
impl<T: 'static> std::error::Error for NotFoundInServerContext<T> {}
pub struct FromContext<T: std::marker::Send + std::marker::Sync + Clone + 'static>(pub(crate) T);
#[async_trait::async_trait(?Send)]
impl<T: Send + Sync + Clone + 'static> FromServerContext for FromContext<T> {
type Rejection = NotFoundInServerContext<T>;
async fn from_request(req: &DioxusServerContext) -> Result<Self, Self::Rejection> {
Ok(Self(req.clone().get::<T>().ok_or_else(|| {
NotFoundInServerContext::<T>(std::marker::PhantomData::<T>)
})?))
}
}
#[cfg(feature = "axum")]
/// An adapter for axum extractors for the server context
pub struct Axum<
I: axum::extract::FromRequestParts<(), Rejection = R>,
R: axum::response::IntoResponse + std::error::Error,
>(pub(crate) I, std::marker::PhantomData<R>);
impl<
I: axum::extract::FromRequestParts<(), Rejection = R>,
R: axum::response::IntoResponse + std::error::Error,
> std::ops::Deref for Axum<I, R>
{
type Target = I;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<
I: axum::extract::FromRequestParts<(), Rejection = R>,
R: axum::response::IntoResponse + std::error::Error,
> std::ops::DerefMut for Axum<I, R>
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[cfg(feature = "axum")]
#[async_trait::async_trait(?Send)]
impl<
I: axum::extract::FromRequestParts<(), Rejection = R>,
R: axum::response::IntoResponse + std::error::Error,
> FromServerContext for Axum<I, R>
{
type Rejection = R;
async fn from_request(req: &DioxusServerContext) -> Result<Self, Self::Rejection> {
Ok(Self(
I::from_request_parts(&mut *req.request_parts_mut().unwrap(), &()).await?,
std::marker::PhantomData,
))
}
}

View file

@ -1,13 +1,11 @@
use crate::server_context::DioxusServerContext;
#[cfg(any(feature = "ssr", doc))]
#[derive(Clone)]
/// A trait object for a function that be called on serializable arguments and returns a serializable result.
pub struct ServerFnTraitObj(server_fn::ServerFnTraitObj<DioxusServerContext>);
pub struct ServerFnTraitObj(server_fn::ServerFnTraitObj<()>);
#[cfg(any(feature = "ssr", doc))]
impl std::ops::Deref for ServerFnTraitObj {
type Target = server_fn::ServerFnTraitObj<DioxusServerContext>;
type Target = server_fn::ServerFnTraitObj<()>;
fn deref(&self) -> &Self::Target {
&self.0
@ -33,9 +31,7 @@ impl ServerFnTraitObj {
}
/// Create a new `ServerFnTraitObj` from a `server_fn::ServerFnTraitObj`.
pub const fn from_generic_server_fn(
server_fn: server_fn::ServerFnTraitObj<DioxusServerContext>,
) -> Self {
pub const fn from_generic_server_fn(server_fn: server_fn::ServerFnTraitObj<()>) -> Self {
Self(server_fn)
}
}
@ -45,7 +41,7 @@ server_fn::inventory::collect!(ServerFnTraitObj);
#[cfg(any(feature = "ssr", doc))]
/// A server function that can be called on serializable arguments and returns a serializable result.
pub type ServerFunction = server_fn::SerializedFnTraitObj<DioxusServerContext>;
pub type ServerFunction = server_fn::SerializedFnTraitObj<()>;
#[cfg(any(feature = "ssr", doc))]
#[allow(clippy::type_complexity)]
@ -64,7 +60,7 @@ static REGISTERED_SERVER_FUNCTIONS: once_cell::sync::Lazy<
pub struct DioxusServerFnRegistry;
#[cfg(feature = "ssr")]
impl server_fn::ServerFunctionRegistry<DioxusServerContext> for DioxusServerFnRegistry {
impl server_fn::ServerFunctionRegistry<()> for DioxusServerFnRegistry {
type Error = ServerRegistrationFnError;
fn register_explicit(
@ -105,7 +101,7 @@ impl server_fn::ServerFunctionRegistry<DioxusServerContext> for DioxusServerFnRe
}
/// Returns the server function registered at the given URL, or `None` if no function is registered at that URL.
fn get(url: &str) -> Option<server_fn::ServerFnTraitObj<DioxusServerContext>> {
fn get(url: &str) -> Option<server_fn::ServerFnTraitObj<()>> {
REGISTERED_SERVER_FUNCTIONS
.read()
.ok()
@ -113,7 +109,7 @@ impl server_fn::ServerFunctionRegistry<DioxusServerContext> for DioxusServerFnRe
}
/// Returns the server function registered at the given URL, or `None` if no function is registered at that URL.
fn get_trait_obj(url: &str) -> Option<server_fn::ServerFnTraitObj<DioxusServerContext>> {
fn get_trait_obj(url: &str) -> Option<server_fn::ServerFnTraitObj<()>> {
Self::get(url)
}
@ -155,7 +151,7 @@ pub enum ServerRegistrationFnError {
/// can be queried on the server for routing purposes by calling [server_fn::ServerFunctionRegistry::get].
///
/// Technically, the trait is implemented on a type that describes the server function's arguments, not the function itself.
pub trait DioxusServerFn: server_fn::ServerFn<DioxusServerContext> {
pub trait DioxusServerFn: server_fn::ServerFn<()> {
/// Registers the server function, allowing the client to query it by URL.
#[cfg(any(feature = "ssr", doc))]
fn register_explicit() -> Result<(), server_fn::ServerFnError> {
@ -163,4 +159,4 @@ pub trait DioxusServerFn: server_fn::ServerFn<DioxusServerContext> {
}
}
impl<T> DioxusServerFn for T where T: server_fn::ServerFn<DioxusServerContext> {}
impl<T> DioxusServerFn for T where T: server_fn::ServerFn<()> {}

View file

@ -170,7 +170,7 @@ impl IncrementalRenderer {
comp: fn(Scope<P>) -> Element,
props: P,
output: &'a mut (impl AsyncWrite + Unpin + Send),
modify_vdom: impl FnOnce(&mut VirtualDom),
rebuild_with: impl FnOnce(&mut VirtualDom),
renderer: &'a R,
) -> impl std::future::Future<Output = Result<RenderFreshness, IncrementalRendererError>> + 'a + Send
{
@ -179,8 +179,7 @@ impl IncrementalRenderer {
let result2;
{
let mut vdom = VirtualDom::new_with_props(comp, props);
modify_vdom(&mut vdom);
let _ = vdom.rebuild();
rebuild_with(&mut vdom);
result_1 = renderer.render_before_body(&mut *html_buffer);
result2 = self.ssr_renderer.render_to(&mut html_buffer, &vdom);
@ -276,7 +275,7 @@ impl IncrementalRenderer {
component: fn(Scope<P>) -> Element,
props: P,
output: &mut (impl AsyncWrite + Unpin + std::marker::Send),
modify_vdom: impl FnOnce(&mut VirtualDom),
rebuild_with: impl FnOnce(&mut VirtualDom),
renderer: &R,
) -> Result<RenderFreshness, IncrementalRendererError> {
// check if this route is cached
@ -285,7 +284,7 @@ impl IncrementalRenderer {
} else {
// if not, create it
let freshness = self
.render_and_cache(route, component, props, output, modify_vdom, renderer)
.render_and_cache(route, component, props, output, rebuild_with, renderer)
.await?;
log::trace!("cache miss");
Ok(freshness)
@ -299,7 +298,7 @@ impl IncrementalRenderer {
component: fn(Scope<P>) -> Element,
props: P,
output: &mut String,
modify_vdom: impl FnOnce(&mut VirtualDom),
rebuild_with: impl FnOnce(&mut VirtualDom),
renderer: &R,
) -> Result<RenderFreshness, IncrementalRendererError> {
unsafe {
@ -309,7 +308,7 @@ impl IncrementalRenderer {
component,
props,
output.as_mut_vec(),
modify_vdom,
rebuild_with,
renderer,
)
.await