Encode Path Parameters in yew-router (#3187)

* Escape for URL in `routable_derive`

* Fixed generic datatypes bug

* FQN

* FQN Again

* remove encode_for_url from public API

* fmt

---------

Co-authored-by: Muhammad Hamza <muhammadhamza1311@gmail.com>
This commit is contained in:
Jedd Dryden 2023-06-21 23:37:25 +10:00 committed by GitHub
parent 0cd979c956
commit 27f537a288
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 104 additions and 3 deletions

7
Cargo.lock generated
View File

@ -2875,6 +2875,12 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "urlencoding"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9"
[[package]]
name = "utf-8"
version = "0.7.6"
@ -3388,6 +3394,7 @@ dependencies = [
"serde",
"serde_urlencoded",
"tracing",
"urlencoding",
"wasm-bindgen",
"wasm-bindgen-test",
"web-sys",

View File

@ -129,10 +129,15 @@ impl Routable {
Fields::Unit => quote! { Self::#ident },
Fields::Named(field) => {
let fields = field.named.iter().map(|it| {
//named fields have idents
// named fields have idents
it.ident.as_ref().unwrap()
});
quote! { Self::#ident { #(#fields: params.get(stringify!(#fields))?.parse().ok()?,)* } }
quote! { Self::#ident { #(#fields: {
let param = params.get(stringify!(#fields))?;
let param = &*::yew_router::__macro::decode_for_url(param).ok()?;
let param = param.parse().ok()?;
param
},)* } }
}
Fields::Unnamed(_) => unreachable!(), // already checked
};
@ -176,7 +181,7 @@ impl Routable {
}
quote! {
Self::#ident { #(#fields),* } => ::std::format!(#right, #(#fields = #fields),*)
Self::#ident { #(#fields),* } => ::std::format!(#right, #(#fields = ::yew_router::__macro::encode_for_url(&::std::format!("{}", #fields))),*)
}
}
Fields::Unnamed(_) => unreachable!(), // already checked

View File

@ -22,6 +22,7 @@ route-recognizer = "0.3"
serde = "1"
serde_urlencoded = "0.7.1"
tracing = "0.1.37"
urlencoding = "2.1.2"
[dependencies.web-sys]
version = "0.3"

View File

@ -1,3 +1,5 @@
pub use urlencoding::{decode as decode_for_url, encode as encode_for_url};
use crate::utils::strip_slash_suffix;
use crate::Routable;

View File

@ -48,3 +48,26 @@ fn router_trailing_slash() {
AppRoute::recognize("/category/cooking-recipes/")
);
}
#[test]
fn router_url_encoding() {
#[derive(Routable, Debug, Clone, PartialEq)]
enum AppRoute {
#[at("/")]
Root,
#[at("/search/:query")]
Search { query: String },
}
assert_eq!(
yew_router::__macro::decode_for_url("/search/a%2Fb/").unwrap(),
"/search/a/b/"
);
assert_eq!(
Some(AppRoute::Search {
query: "a/b".to_string()
}),
AppRoute::recognize("/search/a%2Fb/")
);
}

View File

@ -0,0 +1,63 @@
use std::time::Duration;
use yew::platform::time::sleep;
use yew::prelude::*;
use yew_router::prelude::*;
mod utils;
use utils::*;
use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
wasm_bindgen_test_configure!(run_in_browser);
#[derive(Routable, Debug, Clone, PartialEq)]
enum AppRoute {
#[at("/")]
Root,
#[at("/search/:query")]
Search { query: String },
}
#[function_component]
fn Comp() -> Html {
let switch = move |routes: AppRoute| match routes {
AppRoute::Root => html! {
<>
<h1>{ "Root" }</h1>
<Link<AppRoute> to={AppRoute::Search { query: "a/b".to_string() }}>
{"Click me"}
</Link<AppRoute>>
</>
},
AppRoute::Search { query } => html! {
<p id="q">{ query }</p>
},
};
html! {
<Switch<AppRoute> render={switch} />
}
}
#[function_component(Root)]
fn root() -> Html {
html! {
<BrowserRouter>
<Comp />
</BrowserRouter>
}
}
#[test]
async fn url_encoded_roundtrip() {
yew::Renderer::<Root>::with_root(gloo::utils::document().get_element_by_id("output").unwrap())
.render();
sleep(Duration::ZERO).await;
click("a");
sleep(Duration::ZERO).await;
let res = obtain_result_by_id("q");
assert_eq!(res, "a/b");
assert_eq!(
gloo::utils::window().location().pathname().unwrap(),
"/search/a%2Fb"
)
}