mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
feature-gate the form redirect stuff, and clear old errors from query
This commit is contained in:
parent
90ba3529e9
commit
06c478b7cb
4 changed files with 39 additions and 6 deletions
|
@ -20,7 +20,7 @@ typed-builder = "0.18"
|
|||
typed-builder-macro = "0.18"
|
||||
serde = { version = "1", optional = true }
|
||||
serde_json = { version = "1", optional = true }
|
||||
server_fn = { workspace = true, features = ["browser", "url", "cbor"] }
|
||||
server_fn = { workspace = true, features = ["form-redirects", "browser", "url", "cbor"] }
|
||||
web-sys = { version = "0.3.63", features = [
|
||||
"ShadowRoot",
|
||||
"ShadowRootInit",
|
||||
|
|
|
@ -68,6 +68,7 @@ url = "2"
|
|||
|
||||
[features]
|
||||
default = [ "json", "cbor"]
|
||||
form-redirects = []
|
||||
actix = ["ssr", "dep:actix-web", "dep:send_wrapper"]
|
||||
axum = [
|
||||
"ssr",
|
||||
|
|
|
@ -408,7 +408,7 @@ impl<CustErr> From<ServerFnError<CustErr>> for ServerFnErrorErr<CustErr> {
|
|||
/// found at a particular path.
|
||||
///
|
||||
/// This can be used to pass an error from the server back to the client
|
||||
/// without JavaScript/WASM supported, by encoding it in the URL as a qurey string.
|
||||
/// without JavaScript/WASM supported, by encoding it in the URL as a query string.
|
||||
/// This is useful for progressive enhancement.
|
||||
#[derive(Debug)]
|
||||
pub struct ServerFnUrlError<CustErr> {
|
||||
|
@ -450,6 +450,29 @@ impl<CustErr> ServerFnUrlError<CustErr> {
|
|||
);
|
||||
Ok(url)
|
||||
}
|
||||
|
||||
/// Replaces any ServerFnUrlError info from the URL in the given string
|
||||
/// with the serialized success value given.
|
||||
pub fn strip_error_info(path: &mut String) {
|
||||
if let Ok(mut url) = Url::parse(&*path) {
|
||||
// NOTE: This is gross, but the Serializer you get from
|
||||
// .query_pairs_mut() isn't an Iterator so you can't just .retain().
|
||||
let pairs_previously = url
|
||||
.query_pairs()
|
||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||
.collect::<Vec<_>>();
|
||||
let mut pairs = url.query_pairs_mut();
|
||||
pairs.clear();
|
||||
for (key, value) in pairs_previously
|
||||
.into_iter()
|
||||
.filter(|(key, _)| key != "__path" && key != "__err")
|
||||
{
|
||||
pairs.append_pair(&key, &value);
|
||||
}
|
||||
drop(pairs);
|
||||
*path = url.to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<CustErr> From<ServerFnUrlError<CustErr>> for ServerFnError<CustErr> {
|
||||
|
|
|
@ -238,10 +238,13 @@ where
|
|||
// Server functions can either be called by a real Client,
|
||||
// or directly by an HTML <form>. If they're accessed by a <form>, default to
|
||||
// redirecting back to the Referer.
|
||||
let accepts_html = req
|
||||
.accepts()
|
||||
.map(|n| n.contains("text/html"))
|
||||
.unwrap_or(false);
|
||||
let accepts_html = if cfg!(feature = "form-redirects") {
|
||||
req.accepts()
|
||||
.map(|n| n.contains("text/html"))
|
||||
.unwrap_or(false)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let mut referer = req.referer().as_deref().map(ToOwned::to_owned);
|
||||
|
||||
async move {
|
||||
|
@ -256,6 +259,7 @@ where
|
|||
});
|
||||
|
||||
// if it accepts HTML, we'll redirect to the Referer
|
||||
#[cfg(feature = "form-redirects")]
|
||||
if accepts_html {
|
||||
// if it had an error, encode that error in the URL
|
||||
if let Some(err) = err {
|
||||
|
@ -265,6 +269,11 @@ where
|
|||
referer = Some(url.to_string());
|
||||
}
|
||||
}
|
||||
// otherwise, strip error info from referer URL, as that means it's from a previous
|
||||
// call
|
||||
else if let Some(referer) = referer.as_mut() {
|
||||
ServerFnUrlError::<Self::Error>::strip_error_info(referer)
|
||||
}
|
||||
|
||||
// set the status code and Location header
|
||||
res.redirect(referer.as_deref().unwrap_or("/"));
|
||||
|
|
Loading…
Reference in a new issue