From 4fa72a94fbc3fe28406aed187b5d800db84e2681 Mon Sep 17 00:00:00 2001 From: Greg Johnston Date: Tue, 30 Jul 2024 10:16:34 -0400 Subject: [PATCH] feat: allow reusing the same endpoint for server functions with different HTTP verbs in their input encodings --- integrations/actix/src/lib.rs | 5 +-- integrations/axum/src/lib.rs | 5 ++- server_fn/src/lib.rs | 60 +++++++++++++++++++++++------------ 3 files changed, 46 insertions(+), 24 deletions(-) diff --git a/integrations/actix/src/lib.rs b/integrations/actix/src/lib.rs index c2c6a01a0..98de468e3 100644 --- a/integrations/actix/src/lib.rs +++ b/integrations/actix/src/lib.rs @@ -302,8 +302,9 @@ pub fn handle_server_fns_with_context( let additional_context = additional_context.clone(); let path = req.path(); + let method = req.method(); if let Some(mut service) = - server_fn::actix::get_server_fn_service(path) + server_fn::actix::get_server_fn_service(path, method) { let owner = Owner::new(); owner @@ -1323,7 +1324,7 @@ impl LeptosRoutes for &mut ServiceConfig { let mut router = self; // register server functions first to allow for wildcard route in Leptos's Router - for (path, _) in server_fn::actix::server_fn_paths() { + for (path, method) in server_fn::actix::server_fn_paths() { let additional_context = additional_context.clone(); let handler = handle_server_fns_with_context(additional_context); router = router.route(path, handler); diff --git a/integrations/axum/src/lib.rs b/integrations/axum/src/lib.rs index 2c8679e2c..d5407454f 100644 --- a/integrations/axum/src/lib.rs +++ b/integrations/axum/src/lib.rs @@ -313,10 +313,13 @@ async fn handle_server_fns_inner( ) -> impl IntoResponse { use server_fn::middleware::Service; + let method = req.method().clone(); let path = req.uri().path().to_string(); let (req, parts) = generate_request_and_parts(req); - if let Some(mut service) = server_fn::axum::get_server_fn_service(&path) { + if let Some(mut service) = + server_fn::axum::get_server_fn_service(&path, method) + { let owner = Owner::new(); owner .with(|| { diff --git a/server_fn/src/lib.rs b/server_fn/src/lib.rs index 2d796d683..53301e6d7 100644 --- a/server_fn/src/lib.rs +++ b/server_fn/src/lib.rs @@ -362,7 +362,9 @@ macro_rules! initialize_server_fn_map { once_cell::sync::Lazy::new(|| { $crate::inventory::iter::> .into_iter() - .map(|obj| (obj.path(), obj.clone())) + .map(|obj| { + ((obj.path().to_string(), obj.method()), obj.clone()) + }) .collect() }) }; @@ -442,7 +444,7 @@ impl Clone for ServerFnTraitObj { #[allow(unused)] // used by server integrations type LazyServerFnMap = - Lazy>>; + Lazy>>; #[cfg(feature = "ssr")] impl inventory::Collect @@ -481,7 +483,7 @@ pub mod axum { > + 'static, { REGISTERED_SERVER_FUNCTIONS.insert( - T::PATH, + (T::PATH.into(), T::InputEncoding::METHOD), ServerFnTraitObj::new( T::PATH, T::InputEncoding::METHOD, @@ -502,7 +504,9 @@ pub mod axum { pub async fn handle_server_fn(req: Request) -> Response { let path = req.uri().path(); - if let Some(mut service) = get_server_fn_service(path) { + if let Some(mut service) = + get_server_fn_service(path, req.method().clone()) + { service.run(req).await } else { Response::builder() @@ -524,8 +528,10 @@ pub mod axum { /// Returns the server function at the given path as a service that can be modified. pub fn get_server_fn_service( path: &str, + method: Method, ) -> Option, Response>> { - REGISTERED_SERVER_FUNCTIONS.get(path).map(|server_fn| { + let key = (path.into(), method); + REGISTERED_SERVER_FUNCTIONS.get(&key).map(|server_fn| { let middleware = (server_fn.middleware)(); let mut service = BoxedService::new(server_fn.clone()); for middleware in middleware { @@ -565,7 +571,7 @@ pub mod actix { > + 'static, { REGISTERED_SERVER_FUNCTIONS.insert( - T::PATH, + (T::PATH.into(), T::InputEncoding::METHOD), ServerFnTraitObj::new( T::PATH, T::InputEncoding::METHOD, @@ -588,13 +594,8 @@ pub mod actix { payload: Payload, ) -> HttpResponse { let path = req.uri().path(); - if let Some(server_fn) = REGISTERED_SERVER_FUNCTIONS.get(path) { - let middleware = (server_fn.middleware)(); - // http::Method is the only non-Copy type here - let mut service = BoxedService::new(server_fn.clone()); - for middleware in middleware { - service = middleware.layer(service); - } + let method = req.method(); + if let Some(mut service) = get_server_fn_service(path, method) { service .0 .run(ActixRequest::from((req, payload))) @@ -618,14 +619,31 @@ pub mod actix { /// Returns the server function at the given path as a service that can be modified. pub fn get_server_fn_service( path: &str, + method: &actix_web::http::Method, ) -> Option> { - REGISTERED_SERVER_FUNCTIONS.get(path).map(|server_fn| { - let middleware = (server_fn.middleware)(); - let mut service = BoxedService::new(server_fn.clone()); - for middleware in middleware { - service = middleware.layer(service); - } - service - }) + use actix_web::http::Method as ActixMethod; + + let method = match *method { + ActixMethod::GET => Method::GET, + ActixMethod::POST => Method::POST, + ActixMethod::PUT => Method::PUT, + ActixMethod::PATCH => Method::PATCH, + ActixMethod::DELETE => Method::DELETE, + ActixMethod::HEAD => Method::HEAD, + ActixMethod::TRACE => Method::TRACE, + ActixMethod::OPTIONS => Method::OPTIONS, + ActixMethod::CONNECT => Method::CONNECT, + _ => unreachable!(), + }; + REGISTERED_SERVER_FUNCTIONS.get(&(path.into(), method)).map( + |server_fn| { + let middleware = (server_fn.middleware)(); + let mut service = BoxedService::new(server_fn.clone()); + for middleware in middleware { + service = middleware.layer(service); + } + service + }, + ) } }