If the user visits a path on our Rocket-based site that does not match any of the routes, by default, Rocket will show a very simple page saying 404: Not Found and The requested resource could not be found..
You might want to have a more fancy page.
In this example you can see how to do that.
Dependencies
We only need Rocket for this.
examples/rocket/http-404-page-with-static-content/Cargo.toml
[package]
name = "http-404-page-with-static-content"
version = "0.1.0"
edition = "2021"
[dependencies]
rocket = "0.5.0"
The HTML
We could embed the HTML in our Rust file, but it is better to have it in a separate file with html
extension. That will allow a designer
to make it nice without any interaction with Ruts.
Se we have it in a separate file.
Note: I am not a designer.
examples/rocket/http-404-page-with-static-content/src/templates/404.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes">
<link href="/css/skeleton.css" rel="stylesheet">
<script src="/js/skeleton.js"></script>
<title>404 Page not found</title>
</head>
<body>
<h1>Ooups</h1>
This page was not found.
</body>
</html>
The main code
examples/rocket/http-404-page-with-static-content/src/main.rs
#[macro_use]
extern crate rocket;
use rocket::response::content;
#[get("/")]
fn index() -> content::RawHtml<&'static str> {
content::RawHtml("Hello, <b>world!</b>")
}
#[catch(404)]
fn not_found() -> content::RawHtml<&'static str> {
const BODY: &str = include_str!("templates/404.html");
content::RawHtml(BODY)
}
#[launch]
fn rocket() -> _ {
rocket::build()
.mount("/", routes![index])
.register("/", catchers![not_found])
}
#[cfg(test)]
mod tests;
The test
examples/rocket/http-404-page-with-static-content/src/tests.rs
use rocket::form::validate::Contains;
use rocket::http::Status;
use rocket::local::blocking::Client;
#[test]
fn main_page() {
let client = Client::tracked(super::rocket()).unwrap();
let response = client.get("/").dispatch();
assert_eq!(response.status(), Status::Ok);
assert_eq!(
response.headers().get_one("Content-Type").unwrap(),
"text/html; charset=utf-8"
);
assert_eq!(response.into_string(), Some("Hello, <b>world!</b>".into()));
}
#[test]
fn page_not_found() {
let client = Client::tracked(super::rocket()).unwrap();
let response = client.get("/something").dispatch();
assert_eq!(response.status(), Status::NotFound);
assert_eq!(
response.headers().get_one("Content-Type").unwrap(),
"text/html; charset=utf-8"
);
let html = response.into_string().unwrap();
assert!(html.contains("<title>404 Page not found</title>"));
assert!(html.contains("<h1>Ooups</h1>"));
}
Explanation
We implement the function that will be called by Rocket when all routes were tried and none of them matched.
It looks exactly like a regular route, but it is marked with catch(404)
.
#[catch(404)]
fn not_found() -> RawHtml<&'static str> {
const BODY: &str = include_str!("templates/404.html");
RawHtml(BODY)
}
The include_str! embeds the external file into the compiled binary during compilation.
We also need to register the catchers using the register method and the catchers! macro:
#[launch]
fn rocket() -> _ {
rocket::build()
.mount("/", routes![index])
.register("/", catchers![not_found])
}
In the tests we can verify that the response status for the main route is Ok and for an arbitrary other path is NotFound.
In the main route we checked that the returned page is exactly as we expect.
In the 404 route we demonstrate how to check that some content is in the returned HTML.