feat: allow reusing the same endpoint for server functions with different HTTP verbs in their input encodings

This commit is contained in:
Greg Johnston 2024-07-30 10:16:34 -04:00
parent 24775fb59b
commit 4fa72a94fb
3 changed files with 46 additions and 24 deletions

View File

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

View File

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

View File

@ -362,7 +362,9 @@ macro_rules! initialize_server_fn_map {
once_cell::sync::Lazy::new(|| {
$crate::inventory::iter::<ServerFnTraitObj<$req, $res>>
.into_iter()
.map(|obj| (obj.path(), obj.clone()))
.map(|obj| {
((obj.path().to_string(), obj.method()), obj.clone())
})
.collect()
})
};
@ -442,7 +444,7 @@ impl<Req, Res> Clone for ServerFnTraitObj<Req, Res> {
#[allow(unused)] // used by server integrations
type LazyServerFnMap<Req, Res> =
Lazy<DashMap<&'static str, ServerFnTraitObj<Req, Res>>>;
Lazy<DashMap<(String, Method), ServerFnTraitObj<Req, Res>>>;
#[cfg(feature = "ssr")]
impl<Req: 'static, Res: 'static> 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<Body>) -> Response<Body> {
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<BoxedService<Request<Body>, Response<Body>>> {
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<BoxedService<ActixRequest, ActixResponse>> {
REGISTERED_SERVER_FUNCTIONS.get(path).map(|server_fn| {
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
})
},
)
}
}