update server functions commit
This commit is contained in:
1 parent
7193453fe0
commit
0726a7b08f
8 files changed
+138
-58
No files matched your search
@@ -13,7 +13,7 @@ keywords = ["dom", "ui", "gui", "react", "ssr", "fullstack"]
|
||||
|
||||
[dependencies]
|
||||
# server functions
|
||||
server_fn = { git = "https://github.com/leptos-rs/leptos", rev = "a9e6590b5e7f1c0b01da7db7b86719cb18a4aaa1", features = ["stable"] }
|
||||
server_fn = { git = "https://github.com/leptos-rs/leptos", rev = "671b1e4a8fff7a2e05bb621ef08e87be2b18ccae", features = ["stable"] }
|
||||
dioxus_server_macro = { path = "server-macro" }
|
||||
|
||||
# warp
|
||||
|
||||
@@ -7,7 +7,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
quote = "1.0.26"
|
||||
server_fn_macro = { git = "https://github.com/leptos-rs/leptos", rev = "a9e6590b5e7f1c0b01da7db7b86719cb18a4aaa1", features = ["stable"] }
|
||||
server_fn_macro = { git = "https://github.com/leptos-rs/leptos", rev = "671b1e4a8fff7a2e05bb621ef08e87be2b18ccae", features = ["stable"] }
|
||||
syn = { version = "1", features = ["full"] }
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -14,9 +14,10 @@ use server_fn_macro::*;
|
||||
/// 2. *Optional*: A URL prefix at which the function will be mounted when it’s registered
|
||||
/// (e.g., `"/api"`). Defaults to `"/"`.
|
||||
/// 3. *Optional*: either `"Cbor"` (specifying that it should use the binary `cbor` format for
|
||||
/// serialization) or `"Url"` (specifying that it should be use a URL-encoded form-data string).
|
||||
/// serialization), `"Url"` (specifying that it should be use a URL-encoded form-data string).
|
||||
/// Defaults to `"Url"`. If you want to use this server function to power a `<form>` that will
|
||||
/// work without WebAssembly, the encoding must be `"Url"`.
|
||||
/// work without WebAssembly, the encoding must be `"Url"`. If you want to use this server function
|
||||
/// using Get instead of Post methods, the encoding must be `"GetCbor"` or `"GetJson"`.
|
||||
///
|
||||
/// 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 [DioxusServerContext](https::/docs.rs/dioxus-server/latest/dixous_server/prelude/struct.DioxusServerContext.html),
|
||||
|
||||
@@ -55,10 +55,9 @@
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use std::{error::Error, sync::Arc};
|
||||
|
||||
use axum::{
|
||||
body::{self, Body, BoxBody, Full},
|
||||
extract::RawQuery,
|
||||
extract::{State, WebSocketUpgrade},
|
||||
handler::Handler,
|
||||
http::{HeaderMap, Request, Response, StatusCode},
|
||||
@@ -66,14 +65,13 @@ use axum::{
|
||||
routing::{get, post},
|
||||
Router,
|
||||
};
|
||||
use server_fn::{Payload, ServerFunctionRegistry};
|
||||
use server_fn::{Encoding, Payload, ServerFunctionRegistry};
|
||||
use std::error::Error;
|
||||
use tokio::task::spawn_blocking;
|
||||
|
||||
use crate::{
|
||||
render::SSRState,
|
||||
serve_config::ServeConfig,
|
||||
server_context::DioxusServerContext,
|
||||
server_fn::{DioxusServerFnRegistry, ServerFnTraitObj},
|
||||
prelude::*, render::SSRState, serve_config::ServeConfig, server_context::DioxusServerContext,
|
||||
server_fn::DioxusServerFnRegistry,
|
||||
};
|
||||
|
||||
/// A extension trait with utilities for integrating Dioxus with your Axum router.
|
||||
@@ -106,7 +104,7 @@ pub trait DioxusRouterExt<S> {
|
||||
fn register_server_fns_with_handler<H, T>(
|
||||
self,
|
||||
server_fn_route: &'static str,
|
||||
handler: impl Fn(Arc<ServerFnTraitObj>) -> H,
|
||||
handler: impl Fn(ServerFunction) -> H,
|
||||
) -> Self
|
||||
where
|
||||
H: Handler<T, S>,
|
||||
@@ -200,7 +198,7 @@ where
|
||||
fn register_server_fns_with_handler<H, T>(
|
||||
self,
|
||||
server_fn_route: &'static str,
|
||||
mut handler: impl FnMut(Arc<ServerFnTraitObj>) -> H,
|
||||
mut handler: impl FnMut(ServerFunction) -> H,
|
||||
) -> Self
|
||||
where
|
||||
H: Handler<T, S, Body>,
|
||||
@@ -211,15 +209,22 @@ where
|
||||
for server_fn_path in DioxusServerFnRegistry::paths_registered() {
|
||||
let func = DioxusServerFnRegistry::get(server_fn_path).unwrap();
|
||||
let full_route = format!("{server_fn_route}/{server_fn_path}");
|
||||
router = router.route(&full_route, post(handler(func)));
|
||||
match func.encoding {
|
||||
Encoding::Url | Encoding::Cbor => {
|
||||
router = router.route(&full_route, post(handler(func)));
|
||||
}
|
||||
Encoding::GetJSON | Encoding::GetCBOR => {
|
||||
router = router.route(&full_route, get(handler(func)));
|
||||
}
|
||||
}
|
||||
}
|
||||
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((), func.clone(), headers, body).await
|
||||
move |headers: HeaderMap, RawQuery(query): RawQuery, body: Request<Body>| async move {
|
||||
server_fn_handler((), func.clone(), headers, query, body).await
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -299,8 +304,9 @@ async fn render_handler<P: Clone + Send + Sync + 'static>(
|
||||
/// 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: Arc<ServerFnTraitObj>,
|
||||
function: ServerFunction,
|
||||
headers: HeaderMap,
|
||||
query: Option<String>,
|
||||
req: Request<Body>,
|
||||
) -> impl IntoResponse {
|
||||
let server_context = server_context.into();
|
||||
@@ -317,7 +323,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 =
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use std::{error::Error, sync::Arc};
|
||||
use std::error::Error;
|
||||
|
||||
use hyper::{http::HeaderValue, StatusCode};
|
||||
use salvo::{
|
||||
@@ -58,14 +58,11 @@ use salvo::{
|
||||
serve_static::{StaticDir, StaticFile},
|
||||
Depot, FlowCtrl, Handler, Request, Response, Router,
|
||||
};
|
||||
use server_fn::{Payload, ServerFunctionRegistry};
|
||||
use server_fn::{Encoding, Payload, ServerFunctionRegistry};
|
||||
use tokio::task::spawn_blocking;
|
||||
|
||||
use crate::{
|
||||
prelude::DioxusServerContext,
|
||||
render::SSRState,
|
||||
serve_config::ServeConfig,
|
||||
server_fn::{DioxusServerFnRegistry, ServerFnTraitObj},
|
||||
prelude::*, render::SSRState, serve_config::ServeConfig, server_fn::DioxusServerFnRegistry,
|
||||
};
|
||||
|
||||
/// A extension trait with utilities for integrating Dioxus with your Salvo router.
|
||||
@@ -79,7 +76,7 @@ pub trait DioxusRouterExt {
|
||||
/// use dioxus_server::prelude::*;
|
||||
///
|
||||
/// struct ServerFunctionHandler {
|
||||
/// server_fn: Arc<ServerFnTraitObj>,
|
||||
/// server_fn: ServerFunction,
|
||||
/// }
|
||||
///
|
||||
/// #[handler]
|
||||
@@ -112,7 +109,7 @@ pub trait DioxusRouterExt {
|
||||
fn register_server_fns_with_handler<H>(
|
||||
self,
|
||||
server_fn_route: &'static str,
|
||||
handler: impl Fn(Arc<ServerFnTraitObj>) -> H,
|
||||
handler: impl Fn(ServerFunction) -> H,
|
||||
) -> Self
|
||||
where
|
||||
H: Handler + 'static;
|
||||
@@ -187,7 +184,7 @@ impl DioxusRouterExt for Router {
|
||||
fn register_server_fns_with_handler<H>(
|
||||
self,
|
||||
server_fn_route: &'static str,
|
||||
mut handler: impl FnMut(Arc<ServerFnTraitObj>) -> H,
|
||||
mut handler: impl FnMut(ServerFunction) -> H,
|
||||
) -> Self
|
||||
where
|
||||
H: Handler + 'static,
|
||||
@@ -196,7 +193,14 @@ impl DioxusRouterExt for Router {
|
||||
for server_fn_path in DioxusServerFnRegistry::paths_registered() {
|
||||
let func = DioxusServerFnRegistry::get(server_fn_path).unwrap();
|
||||
let full_route = format!("{server_fn_route}/{server_fn_path}");
|
||||
router = router.push(Router::with_path(&full_route).post(handler(func)));
|
||||
match func.encoding {
|
||||
Encoding::Url | Encoding::Cbor => {
|
||||
router = router.push(Router::with_path(&full_route).post(handler(func)));
|
||||
}
|
||||
Encoding::GetJSON | Encoding::GetCBOR => {
|
||||
router = router.push(Router::with_path(&full_route).get(handler(func)));
|
||||
}
|
||||
}
|
||||
}
|
||||
router
|
||||
}
|
||||
@@ -288,15 +292,12 @@ impl<P: Clone + Send + Sync + 'static> Handler for SSRHandler<P> {
|
||||
/// 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<ServerFnTraitObj>,
|
||||
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<DioxusServerContext>,
|
||||
function: Arc<ServerFnTraitObj>,
|
||||
) -> Self {
|
||||
pub fn new(server_context: impl Into<DioxusServerContext>, 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();
|
||||
})
|
||||
|
||||
@@ -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<H, F, R>(
|
||||
mut handler: H,
|
||||
) -> BoxedFilter<(R,)>
|
||||
where
|
||||
H: FnMut(String, Arc<ServerFnTraitObj>) -> F,
|
||||
H: FnMut(String, ServerFunction) -> F,
|
||||
F: Filter<Extract = (R,), Error = warp::Rejection> + 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<DioxusServerContext>,
|
||||
function: Arc<ServerFnTraitObj>,
|
||||
function: ServerFunction,
|
||||
headers: HeaderMap,
|
||||
query: Option<String>,
|
||||
body: Bytes,
|
||||
) -> Result<Box<dyn warp::Reply>, 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 =
|
||||
|
||||
@@ -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};
|
||||
}
|
||||
@@ -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<DioxusServerContext>;
|
||||
|
||||
#[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<DioxusServerContext>;
|
||||
|
||||
#[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<ServerFnTraitObj>>,
|
||||
>,
|
||||
>,
|
||||
std::sync::Arc<std::sync::RwLock<std::collections::HashMap<&'static str, ServerFunction>>>,
|
||||
> = once_cell::sync::Lazy::new(Default::default);
|
||||
|
||||
#[cfg(any(feature = "ssr", doc))]
|
||||
@@ -25,12 +25,19 @@ impl server_fn::ServerFunctionRegistry<DioxusServerContext> for DioxusServerFnRe
|
||||
fn register(
|
||||
url: &'static str,
|
||||
server_function: std::sync::Arc<ServerFnTraitObj>,
|
||||
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<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<std::sync::Arc<ServerFnTraitObj>> {
|
||||
fn get(url: &str) -> Option<ServerFunction> {
|
||||
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<std::sync::Arc<ServerFnTraitObj>> {
|
||||
REGISTERED_SERVER_FUNCTIONS
|
||||
.read()
|
||||
.ok()
|
||||
.and_then(|fns| fns.get(url).map(|f| f.trait_obj.clone()))
|
||||
}
|
||||
|
||||
fn get_encoding(url: &str) -> Option<server_fn::Encoding> {
|
||||
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
|
||||
|
||||
Reference in new issue
Block a user