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: Arc,
+ function: ServerFunction,
}
impl ServerFnHandler {
/// Create a new server function handler with the given server context and server function.
- pub fn new(
- server_context: impl Into,
- function: Arc,
- ) -> Self {
+ pub fn new(server_context: impl Into, function: ServerFunction) -> Self {
let server_context = server_context.into();
Self {
server_context,
@@ -324,12 +325,23 @@ impl ServerFnHandler {
let (resp_tx, resp_rx) = tokio::sync::oneshot::channel();
let function = function.clone();
let server_context = server_context.clone();
+ let query = req
+ .uri()
+ .query()
+ .unwrap_or_default()
+ .as_bytes()
+ .to_vec()
+ .into();
spawn_blocking({
move || {
tokio::runtime::Runtime::new()
.expect("couldn't spawn runtime")
.block_on(async move {
- let resp = function(server_context, &body).await;
+ let data = match &function.encoding {
+ Encoding::Url | Encoding::Cbor => &body,
+ Encoding::GetJSON | Encoding::GetCBOR => &query,
+ };
+ let resp = (function.trait_obj)(server_context, data).await;
resp_tx.send(resp).unwrap();
})
diff --git a/packages/server/src/adapters/warp_adapter.rs b/packages/server/src/adapters/warp_adapter.rs
index 5041e294d..db88ac2b4 100644
--- a/packages/server/src/adapters/warp_adapter.rs
+++ b/packages/server/src/adapters/warp_adapter.rs
@@ -47,9 +47,12 @@
//!
//! ```
-use std::{error::Error, sync::Arc};
+use crate::{
+ prelude::*, render::SSRState, serve_config::ServeConfig, server_fn::DioxusServerFnRegistry,
+};
-use server_fn::{Payload, ServerFunctionRegistry};
+use server_fn::{Encoding, Payload, ServerFunctionRegistry};
+use std::error::Error;
use tokio::task::spawn_blocking;
use warp::{
filters::BoxedFilter,
@@ -58,13 +61,6 @@ use warp::{
path, Filter, Reply,
};
-use crate::{
- prelude::DioxusServerContext,
- render::SSRState,
- serve_config::ServeConfig,
- server_fn::{DioxusServerFnRegistry, ServerFnTraitObj},
-};
-
/// Registers server functions with a custom handler function. This allows you to pass custom context to your server functions by generating a [`DioxusServerContext`] from the request.
///
/// # Example
@@ -94,7 +90,7 @@ pub fn register_server_fns_with_handler(
mut handler: H,
) -> BoxedFilter<(R,)>
where
- H: FnMut(String, Arc) -> F,
+ H: FnMut(String, ServerFunction) -> F,
F: Filter + Send + Sync + 'static,
F::Extract: Send,
R: Reply + 'static,
@@ -129,16 +125,48 @@ where
/// ```
pub fn register_server_fns(server_fn_route: &'static str) -> BoxedFilter<(impl Reply,)> {
register_server_fns_with_handler(server_fn_route, |full_route, func| {
- path(full_route)
- .and(warp::post())
+ let func2 = func.clone();
+ let func3 = func.clone();
+ path(full_route.clone())
+ .and(warp::filters::method::get())
.and(warp::header::headers_cloned())
+ .and(warp::filters::query::raw())
.and(warp::body::bytes())
- .and_then(move |headers: HeaderMap, body| {
+ .and_then(move |headers, query, body| {
let func = func.clone();
async move {
- server_fn_handler(DioxusServerContext::default(), func, headers, body).await
+ server_fn_handler(
+ DioxusServerContext::default(),
+ func,
+ headers,
+ Some(query),
+ body,
+ )
+ .await
}
})
+ .or(path(full_route.clone())
+ .and(warp::filters::method::get())
+ .and(warp::header::headers_cloned())
+ .and(warp::body::bytes())
+ .and_then(move |headers, body| {
+ let func = func2.clone();
+ async move {
+ server_fn_handler(DioxusServerContext::default(), func, headers, None, body)
+ .await
+ }
+ }))
+ .or(path(full_route)
+ .and(warp::filters::method::post())
+ .and(warp::header::headers_cloned())
+ .and(warp::body::bytes())
+ .and_then(move |headers, body| {
+ let func = func3.clone();
+ async move {
+ server_fn_handler(DioxusServerContext::default(), func, headers, None, body)
+ .await
+ }
+ }))
})
}
@@ -198,8 +226,9 @@ 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,
- function: Arc,
+ function: ServerFunction,
headers: HeaderMap,
+ query: Option,
body: Bytes,
) -> Result, warp::Rejection> {
let server_context = server_context.into();
@@ -210,7 +239,12 @@ pub async fn server_fn_handler(
tokio::runtime::Runtime::new()
.expect("couldn't spawn runtime")
.block_on(async {
- let resp = match function(server_context, &body).await {
+ let query = &query.unwrap_or_default().into();
+ let data = match &function.encoding {
+ Encoding::Url | Encoding::Cbor => &body,
+ Encoding::GetJSON | Encoding::GetCBOR => query,
+ };
+ let resp = match (function.trait_obj)(server_context, &data).await {
Ok(serialized) => {
// if this is Accept: application/json then send a serialized JSON response
let accept_header =
diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs
index df2735cf4..525b636b3 100644
--- a/packages/server/src/lib.rs
+++ b/packages/server/src/lib.rs
@@ -30,7 +30,7 @@ pub mod prelude {
pub use crate::server_context::DioxusServerContext;
pub use crate::server_fn::ServerFn;
#[cfg(feature = "ssr")]
- pub use crate::server_fn::ServerFnTraitObj;
+ pub use crate::server_fn::{ServerFnTraitObj, ServerFunction};
pub use dioxus_server_macro::*;
pub use server_fn::{self, ServerFn as _, ServerFnError};
}
diff --git a/packages/server/src/server_fn.rs b/packages/server/src/server_fn.rs
index 643f25c9f..da7311263 100644
--- a/packages/server/src/server_fn.rs
+++ b/packages/server/src/server_fn.rs
@@ -4,14 +4,14 @@ use crate::server_context::DioxusServerContext;
/// A trait object for a function that be called on serializable arguments and returns a serializable result.
pub type ServerFnTraitObj = server_fn::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::ServerFunction;
+
#[cfg(any(feature = "ssr", doc))]
#[allow(clippy::type_complexity)]
static REGISTERED_SERVER_FUNCTIONS: once_cell::sync::Lazy<
- std::sync::Arc<
- std::sync::RwLock<
- std::collections::HashMap<&'static str, std::sync::Arc>,
- >,
- >,
+ std::sync::Arc>>,
> = once_cell::sync::Lazy::new(Default::default);
#[cfg(any(feature = "ssr", doc))]
@@ -25,12 +25,19 @@ impl server_fn::ServerFunctionRegistry for DioxusServerFnRe
fn register(
url: &'static str,
server_function: std::sync::Arc,
+ encoding: server_fn::Encoding,
) -> Result<(), Self::Error> {
// store it in the hashmap
let mut write = REGISTERED_SERVER_FUNCTIONS
.write()
.map_err(|e| ServerRegistrationFnError::Poisoned(e.to_string()))?;
- let prev = write.insert(url, server_function);
+ let prev = write.insert(
+ url,
+ ServerFunction {
+ trait_obj: server_function,
+ encoding,
+ },
+ );
// if there was already a server function with this key,
// return Err
@@ -47,13 +54,28 @@ impl server_fn::ServerFunctionRegistry 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> {
+ fn get(url: &str) -> Option {
REGISTERED_SERVER_FUNCTIONS
.read()
.ok()
.and_then(|fns| fns.get(url).cloned())
}
+ /// 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> {
+ REGISTERED_SERVER_FUNCTIONS
+ .read()
+ .ok()
+ .and_then(|fns| fns.get(url).map(|f| f.trait_obj.clone()))
+ }
+
+ fn get_encoding(url: &str) -> Option {
+ REGISTERED_SERVER_FUNCTIONS
+ .read()
+ .ok()
+ .and_then(|fns| fns.get(url).map(|f| f.encoding.clone()))
+ }
+
/// Returns a list of all registered server functions.
fn paths_registered() -> Vec<&'static str> {
REGISTERED_SERVER_FUNCTIONS