浏览代码

update server functions commit

Evan Almloff 2 年之前
父节点
当前提交
0726a7b08f

+ 1 - 1
packages/server/Cargo.toml

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

+ 1 - 1
packages/server/server-macro/Cargo.toml

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

+ 3 - 2
packages/server/server-macro/src/lib.rs

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

+ 25 - 14
packages/server/src/adapters/axum_adapter.rs

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

+ 28 - 16
packages/server/src/adapters/salvo_adapter.rs

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

+ 50 - 16
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<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 =

+ 1 - 1
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};
 }

+ 29 - 7
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<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