I am using Rocket to create a basic web api to practice and improve my Rust programming skills.
Here I got stuck.
#[post("/make/<link>")]
fn shorten_link(link: String) -> String {
// Some other code
}
The problem is when I post to let's say http://localhost:8000/make/https://youtube.com/
I get redirected to a Rocket 404 page but when I do something like http://localhost:8000/make/hello
everything works fine. I don't understand what I am doing wrong here so any help will be appreciated, thank you.
The problem is that the /
s in the url https://youtube.com/
are being misinterpreted. Specifically, they are being interpreted as a new segment of the path. For example, if I requested /seg1/seg2/seg3
, I wouldn't expect a handler annotated with #[get("/seg1/<arg>")]
to match that path, as there is an extra segment at the end of it. Similarly, the /
s in the https://youtube.com/
are being interpreted as new path segments, and thus aren't hitting your handler.
The first solution I thought of for this was to rewrite the handler like so:
#[post("/make/<link..>")]
fn shorten_link(link: PathBuf) -> String {
let link = link.to_str().unwrap(); // Don't actually unwrap
// Other code
}
I thought this would work, because it takes a whole bunch of path segments, and turns it into one string, but that didn't work, because Rocket complained that you can't end a path segment with a :
.
So I just tried URL-formatting https://youtube.com/
, which worked without any modification to the handler. URL-formatting is where you take symbols that either aren't safe for HTTP or have a different meaning (in our case, /
and :
), and you replace them with a special code that is okay. For example, the URL-encoded version of https://youtube.com/
is https%3A%2F%2Fyoutube.com%2F
. The final request was a POST request to /make/https%3A%2F%2Fyoutube.com%2F
:
client.post(format!(
"/make/{}",
urlencoding::encode("https://youtube.com/"),
));
You can use the urlencoding
crate to encode URLS for you.
Edit: My full code looked like this (it was just a simple test):
#![feature(decl_macro)]
use rocket::{local::Client, post, routes};
#[post("/make/<link>")]
fn shorten_link(link: String) -> String {
link
}
fn main() {
let rocket = rocket::ignite().mount("/", routes![shorten_link]);
let client = Client::new(rocket).expect("valid rocket instance");
let req = client.post(format!(
"/make/{}",
urlencoding::encode("https://youtube.com/"),
));
let mut resp = req.dispatch();
println!("{}", resp.body_string().unwrap());
}