-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Static file serving #239
Comments
I am not sure if this helps.. Have you seen the the |
Indeed, this is covered in the static_files example. |
I had not seen that, thanks! I did eventually find the bit about segments parameters which has that same code as an example, but I still think a helper that simplified this extremely common use case would be nice. It took me a while to find that part of the documentation because I was looking around for "static file serving" and I did not expect to find it under "Dynamic Segments". :) |
@luser Perhaps something in use rocket_contrib::StaticFiles;
rocket::ignite().mount("/path_to_serve_on", StaticFiles::from("/path_to_serve_from")); This would presumably serve files from the I think something like this would be nice, though I'm not sure if it's actually possible to do this in a clean way at the moment. I'll look into it. |
That sounds great! |
I think the way to do it right now is sufficiently simple, just showing how in the guide under "Serving Static Files" or something alike would suffice. |
I think it would be best to offer something that supports HTTP caching. |
@marti1125 Your browser is sending a request for the source map files (*.map) but they don't exist in the directory. Presumably you have developer tools open. |
@SergioBenitez yep it is =D |
Without actually running it, it looks like the static files example would be vulnerable to a path traversal attack. Is there anything preventing this? |
@quadrupleslap See the docs here and here. |
Since the provided example was not enough for, since it didn't even support the Last-Modified header, I wrote my own Fairing for rocket. Maybe we could build upon this, stabilize it somewhere in the future and get it in contrib then. https://crates.io/crates/rocket_static_fs Sadly docs.rs doesn't build the docs cause of an outdated rustc version. But basically the example from the docs shares your apps src folder under /src/:
|
I just remembered that tilpner/includedir is another solution to this, if you don't mind pulling your entire website into your executable. Edit: it's definitely not a comprehensive solution, but it's an interesting place to start. |
@quadrupleslap Including everything is the binary is cool, but this also doesn't handle things like |
@SergioBenitez Is it still planned to allow paths starting with a single dot as described in #560 (comment) ?
Doing #![feature(plugin)]
#![plugin(rocket_codegen)]
extern crate rocket;
extern crate rocket_contrib;
use std::path::{Path, PathBuf};
use std::fs;
use rocket::response::{NamedFile, Responder};
use rocket::response::content::Html;
use rocket::{Request, Response};
use rocket::http::Status;
use rocket::config::{Config, Environment};
fn list_dir<P: AsRef<Path>>(dir: P) -> String {
let dir = dir.as_ref();
let rows = fs::read_dir(&dir).ok().map_or_else(|| "error reading dir".to_string(), |x| x.filter_map(|e| e.ok()).map(|entry| {
let abs_path = entry.path();
let rel_path = abs_path.strip_prefix(dir).unwrap();
let path_str = rel_path.to_string_lossy();
let url = path_str.to_string() + if abs_path.is_dir() { "/" } else { "" };
format!(r#"<li><a href="{}">{}</a>"#, url, url)
}).collect::<String>());
let dir = dir.to_string_lossy();
let dir = if dir == "." { "/".into() } else { dir.replace('\\', "/") };
format!(r#"
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>
<title>Directory listing for {}</title>
<body>
<h2>Directory listing for {}</h2>
<hr>
<ul>
{}
</ul>
<hr>
</body>
</html>
"#, dir, dir, rows)
}
#[get("/")]
fn index() -> Html<String> {
Html(list_dir("."))
}
enum PathResp { File(PathBuf), Dir(PathBuf) }
impl Responder<'static> for PathResp {
fn respond_to(self, req: &Request) -> Result<Response<'static>, Status> {
match self {
PathResp::File(path) => NamedFile::open(path).ok().respond_to(req),
PathResp::Dir(path) => Html(list_dir(&path)).respond_to(req)
}
}
}
#[get("/<path..>")]
fn path(path: PathBuf) -> PathResp {
if path.is_dir() { PathResp::Dir(path) } else { PathResp::File(path) }
}
fn main() {
let config = Config::build(Environment::Staging)
.address("0.0.0.0").port(8000).finalize().unwrap();
rocket::custom(config, true).mount("/", routes![index, path]).launch();
} (The |
@Boscop In case you just need a simplistic, yet functional static file server for rocket, you may want to take a look at https://crates.io/crates/rocket_static_fs |
@SergioBenitez I tried to use the static_files example to setup a static site while supporting other api endpoints as well. However I get this collisions error when loading it up with cargo run:
So basically I have setup the static serving on / like #[get("/")]
fn index() -> io::Result<NamedFile> {
NamedFile::open("dist/index.html")
}
// allow html to reference any file with path /static under folder "static"
#[get("/<file..>")]
fn files(file: PathBuf) -> Option<NamedFile> {
NamedFile::open(Path::new("dist/").join(file)).ok()
} Then added some api endpoint like #[get("/api/upload/<id>")]
fn retrieve(id: PasteID) -> Option<File> {
let filename = format!("tmp/{id}", id = id);
File::open(&filename).ok()
} BTW, the static html and js files are build/generated by a vue project, which as a single index.html and it reference all the built js and css directly via How can I solve this? 🤔 Thank you. |
Oh I just looked carefully at #723 again it seems I can add rank to solve it. Is this the recommended way? |
With ec130f9, you can now do the following: use rocket_contrib::static_files::StaticFiles;
fn main() {
rocket::ignite()
.mount("/path_to_serve_on", StaticFiles::from("/path_to_serve_from"))
.launch();
} |
This sound awesome, will give a try :D Thank you @SergioBenitez 💯 |
@SergioBenitez Hey Sergio again, if I would like to make some user authentication, so that for example if a user has not logged in and want to visit /, then the user will be redirected to some login page (so that all the static files cannot be accessed), I cannot use your recommended way above, right? I guess I will have to use something like: #[get("/")]
fn index(_user: User) -> io::Result<NamedFile> {
NamedFile::open("dist/index.html")
}
// allow html to reference any file with path /static under folder "static"
#[get("/<file..>", rank = 10)] // use rank here to allow other api endpoint available as well
fn files(file: PathBuf, _user: User) -> Option<NamedFile> {
NamedFile::open(Path::new("dist/").join(file)).ok()
} with Does my thoughts make any sense to you? Thanks in advance :) |
@liufuyang I'm not sure I've understood what you're saying. I think you're saying that you want:
If that's the case, then, just as would be the case in any other web framework with a pluggable static-files server, you're likely better off handling this yourself. In Rocket, the way you've illustrated looks about right. |
@SergioBenitez Thank you. |
What happened to the last request? Was handling range header ever implemented? |
First off: let me just say that Rocket is very nice and has been a pleasure to use, kudos!
I looked around the docs and couldn't find any support for serving a set of static files from a directory. This is something that I've seen supported in every web framework I've ever used. As an arbitrary example, here's how it works in Flask. This is a pretty common pattern to serve things like CSS files and JS scripts, so I think it would be useful to have. Obviously one can serve individual files by just having a handler that returns a
File
, but this seems like generally useful functionality. Ideally one would simply be able to do something likerocket::ignite().mount("/static", StaticFiles(some_path)
.The text was updated successfully, but these errors were encountered: