1

I'm creating a portfolio website and some projects have static HTML demos which I'd like to serve according to the ID in the URL. The route looks like this:

#[get("/demo/<id>/<pathbuf..>")]
fn site_demo(id: usize, pathbuf: Option<PathBuf>) -> Option<NamedFile> {
    // set path according to id
    let demo = format!{"static/projects/{:03}/demo/", id};
    // if `pathbuf` is not provided, set file to `index.html`
    let pathbuf = pathbuf.unwrap_or(PathBuf::from("index.html"));

    let path = Path::new(&demo).join(pathbuf);
    NamedFile::open(path).ok()
}

When I type localhost:5050/demo/003/index.html in my browser, the demo (and everything else in the demo folder) gets loaded. However, once I type just localhost:5050/demo/003/ I get this error (same result without / at the end):

GET /demo/003/ text/html:
    => Error: No matching routes for GET /demo/003/ text/html.
    => Warning: Responding with 404 Not Found catcher.
    => Response succeeded.

I'd expect the route to match, because the PathBuf is optional and gets set to index.html. Would make sense to me...

Did I go wrong somewhere or should I open an issue?

1 Answers1

0

A multiple segments path cannot be empty.

A different approach is to use 2 routes :

  • one for the multiple segment /demo/<id>/<pathbuf..>
  • one for the empty segment /demo/<id> that redirect to /demo/<id>/index.html

A sample using rust nightly and rocket 0.4 :

#![feature(proc_macro_hygiene, decl_macro)]  
#[macro_use] extern crate rocket;

use std::path::{Path,PathBuf};
use rocket::response::{Redirect,NamedFile};

#[get("/demo/<id>/<pathbuf..>")]
fn site_demo(id: usize, pathbuf: PathBuf) -> Option<NamedFile> {
    let demo = format!{"static/projects/{:03}/demo/", id};
    NamedFile::open(Path::new(&demo).join(pathbuf)).ok()
}

#[get("/demo/<pathbuf..>", rank=2)]
fn redirect(pathbuf: PathBuf) -> Redirect {
    Redirect::to(format!{"/demo/{}/index.html", pathbuf.display()})
}

fn main() {
    rocket::ignite().mount("/", routes![site_demo,redirect]).launch();
}

A sample using rust stable and rocket 0.5 :

#[macro_use] extern crate rocket;

use std::path::{Path,PathBuf};
use rocket::response::{Redirect,NamedFile};

#[get("/demo/<id>/<pathbuf..>")]
async fn site_demo(id: usize, pathbuf: PathBuf) -> Option<NamedFile> {
    let demo = format!{"static/projects/{:03}/demo/", id};
    NamedFile::open(Path::new(&demo).join(pathbuf)).await.ok()
}

#[get("/demo/<pathbuf..>", rank=2)]
fn redirect(pathbuf: PathBuf) -> Redirect {
    Redirect::to(format!{"/demo/{}/index.html", pathbuf.display()})
}

#[launch]
fn rocket() -> rocket::Rocket {
    rocket::ignite().mount("/", routes![site_demo,redirect])
}

Like this localhost:5050/demo/003/ will be redirect to localhost:5050/demo/003/index.html and then localhost:5050/demo/003/index.html will load static/projects/003/demo/index.html

mpromonet
  • 11,326
  • 43
  • 62
  • 91
  • When I tried this, I got `conflicting implementation for "rocket::StaticRouteInfo"` errors. – Jonathan Mayer Jan 01 '21 at 16:21
  • @JonathanMayer: I just try with `rustc 1.51.0-nightly` and `rocket v0.4.6` and the proposed answer still work. Which version are you using ? – mpromonet Jan 02 '21 at 15:51
  • I'll try again this evening. Possibly, the difference in my environment is that my paths are: "/foo/" vs "/foo/?bar=" Will retest tonight with nightly and report back. – Jonathan Mayer Jan 03 '21 at 18:11
  • This answer propose a solution to add index.html to a path if not specified, your case may be different – mpromonet Jan 03 '21 at 18:55