feat: allow multipart forms on server fns (Actix) (#1048)
This commit is contained in:
parent
43990b5b67
commit
b2faa6b86c
|
@ -8,6 +8,7 @@ repository = "https://github.com/leptos-rs/leptos"
|
|||
description = "Actix integrations for the Leptos web framework."
|
||||
|
||||
[dependencies]
|
||||
actix-http = "3"
|
||||
actix-web = "4"
|
||||
futures = "0.3"
|
||||
leptos = { workspace = true, features = ["ssr"] }
|
||||
|
|
|
@ -185,7 +185,7 @@ pub fn handle_server_fns_with_context(
|
|||
.and_then(|value| value.to_str().ok());
|
||||
|
||||
if let Some(server_fn) = server_fn_by_path(path.as_str()) {
|
||||
let body: &[u8] = &body;
|
||||
let body_ref: &[u8] = &body;
|
||||
|
||||
let runtime = create_runtime();
|
||||
let (cx, disposer) = raw_scope_and_disposer(runtime);
|
||||
|
@ -198,10 +198,28 @@ pub fn handle_server_fns_with_context(
|
|||
provide_context(cx, req.clone());
|
||||
provide_context(cx, res_options.clone());
|
||||
|
||||
// we consume the body here (using the web::Bytes extractor), but it is required for things
|
||||
// like MultipartForm
|
||||
if req
|
||||
.headers()
|
||||
.get("Content-Type")
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.and_then(|value| {
|
||||
Some(
|
||||
value.starts_with(
|
||||
"multipart/form-data; boundary=",
|
||||
),
|
||||
)
|
||||
})
|
||||
== Some(true)
|
||||
{
|
||||
provide_context(cx, body.clone());
|
||||
}
|
||||
|
||||
let query = req.query_string().as_bytes();
|
||||
|
||||
let data = match &server_fn.encoding {
|
||||
Encoding::Url | Encoding::Cbor => body,
|
||||
Encoding::Url | Encoding::Cbor => body_ref,
|
||||
Encoding::GetJSON | Encoding::GetCBOR => query,
|
||||
};
|
||||
let res = match (server_fn.trait_obj)(cx, data).await {
|
||||
|
@ -1072,9 +1090,17 @@ where
|
|||
{
|
||||
let req = use_context::<actix_web::HttpRequest>(cx)
|
||||
.expect("HttpRequest should have been provided via context");
|
||||
let input = E::extract(&req)
|
||||
.await
|
||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))?;
|
||||
|
||||
let input = if let Some(body) = use_context::<Bytes>(cx) {
|
||||
let (_, mut payload) = actix_http::h1::Payload::create(false);
|
||||
payload.unread_data(body);
|
||||
E::from_request(&req, &mut dev::Payload::from(payload))
|
||||
} else {
|
||||
E::extract(&req)
|
||||
}
|
||||
.await
|
||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))?;
|
||||
|
||||
Ok(f.call(input).await)
|
||||
}
|
||||
|
||||
|
|
|
@ -95,8 +95,63 @@ where
|
|||
let action = use_resolved_path(cx, move || action.clone())
|
||||
.get()
|
||||
.unwrap_or_default();
|
||||
// multipart POST (setting Context-Type breaks the request)
|
||||
if method == "post" && enctype == "multipart/form-data" {
|
||||
ev.prevent_default();
|
||||
ev.stop_propagation();
|
||||
|
||||
let on_response = on_response.clone();
|
||||
spawn_local(async move {
|
||||
let res = gloo_net::http::Request::post(&action)
|
||||
.header("Accept", "application/json")
|
||||
.redirect(RequestRedirect::Follow)
|
||||
.body(form_data)
|
||||
.send()
|
||||
.await;
|
||||
match res {
|
||||
Err(e) => {
|
||||
error!("<Form/> error while POSTing: {e:#?}");
|
||||
if let Some(error) = error {
|
||||
error.set(Some(Box::new(e)));
|
||||
}
|
||||
}
|
||||
Ok(resp) => {
|
||||
if let Some(version) = action_version {
|
||||
version.update(|n| *n += 1);
|
||||
}
|
||||
if let Some(error) = error {
|
||||
error.set(None);
|
||||
}
|
||||
if let Some(on_response) = on_response.clone() {
|
||||
on_response(resp.as_raw());
|
||||
}
|
||||
// Check all the logical 3xx responses that might
|
||||
// get returned from a server function
|
||||
if resp.redirected() {
|
||||
let resp_url = &resp.url();
|
||||
match Url::try_from(resp_url.as_str()) {
|
||||
Ok(url) => {
|
||||
request_animation_frame(move || {
|
||||
if let Err(e) = navigate(
|
||||
&format!(
|
||||
"{}{}",
|
||||
url.pathname, url.search,
|
||||
),
|
||||
Default::default(),
|
||||
) {
|
||||
warn!("{}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
Err(e) => warn!("{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// POST
|
||||
if method == "post" {
|
||||
else if method == "post" {
|
||||
ev.prevent_default();
|
||||
ev.stop_propagation();
|
||||
|
||||
|
|
Loading…
Reference in New Issue