Strumenti Utente

Strumenti Sito


programmazione:rust:rocket:appunti

Appunti sul framework web Rocket (Rust)

Autore: Fabio Di Matteo
Ultima revisione: 13/05/2026 11:08

Alcuni pezzettini di codice che possono aiutare a iniziare un progetto.

Hello world

#[macro_use]
extern crate rocket;
use rocket::response::content::RawHtml;
 
use rocket::http::Status;
use rocket::response::Redirect;
use rocket::response::status::Custom;
 
 
 
//Restituisco stringa semplice
#[get("/")]
fn index() -> &'static str {
    "Hello, world!"
}
 
//restituisco html
#[get("/fabio", format = "text/html")]
fn fabio() -> RawHtml<&'static str> {
    RawHtml("<h2>Fabio</h2>")
}
 
#[launch]
fn rocket() -> _ {
    rocket::build().mount("/", routes![index, fabio])
}

Ricezioni dati da un form con metodo POST

#[derive(FromForm)]
struct MyForm {
    nome: String,
    email: String,
}
 
#[post("/invia", data = "<form>")]
fn ricevi(form: Form<MyForm>) -> RawHtml<String> {
    let data = form.into_inner();
 
    let nome = &data.nome;
    let email = &data.email;
    RawHtml(
        format!(
            "<h3>Ricezione dati dal form:</h3><p>{}, {} </p>",
            nome, email
        )
        .to_string(),
    )
}

Possiamo mstrare il form con il seguente codice:

#[get("/show_form")]
fn show_form() -> RawHtml<&'static str> {
    RawHtml(
        r#"<!DOCTYPE html>
<html>
  <head><meta charset="utf-8"><title>Form</title></head>
  <body>
    <form action="/invia" method="post">
      <label>Nome: <input name="nome" /></label><br/>
      <label>Email: <input name="email" /></label><br/>
      <button type="submit">Invia</button>
    </form>
  </body>
</html>"#,
    )
}

Redirect

Un semplice redirect

use rocket::response::Redirect;
 
#[get("/fml")]
fn redirect() -> Redirect {
    Redirect::to("https://www.freemedialab.org/wiki")
}

404 personalizzato

#[catch(404)]
fn not_found_catcher() -> RawHtml<&'static str> {
    RawHtml(
        r#"<!doctype html>
<html><head><meta charset="utf-8"><title>404</title></head>
<body><h1>404 — Pagina non trovata</h1></body>
</html>"#,
    )
}
 
...
 
 
#[launch]
fn rocket() -> _ {
    rocket::build()
        .mount("/", routes![index])
        .register("/", catchers![not_found_catcher]) //il mio catcher per 404
}

Configurare il server

Il server puo' essere configurato in vari modi (Configuration). Io ho usato il file di configurazione “Rocket.toml” nella cartella corrente.

Rocket.toml

[default]
address = "127.0.0.1"
port = 8080

Restituire Status delle richieste

use rocket::http::Status;
use rocket::response::Redirect;
use rocket::response::status::Custom;
 
#[get("/forbidden")]
fn forbidden() -> Custom<RawHtml<String>> {
    let body = r#"<!doctype html>
<html><head><meta charset="utf-8"><title>404</title></head>
<body><h1>Forbidden</h1><p>La risorsa richiesta non puo' essere mostrata.</p></body>
</html>"#
        .to_string();
    Custom(Status::Forbidden, RawHtml(body))
}

Tabella dei codici di stato

Restituire come sopra con :

...
Custom(Status::Forbidden, RawHtml(body))
Code Nome (enum) Significato breve
100 Status::Continue Il client può continuare con la richiesta.
101 Status::SwitchingProtocols Il server cambia protocollo come richiesto.
102 Status::Processing Elaborazione in corso (WebDAV).
103 Status::EarlyHints Suggerimenti preliminari (Link header).
200 Status::Ok Richiesta completata con successo.
201 Status::Created Risorsa creata.
202 Status::Accepted Richiesta accettata per elaborazione asincrona.
203 Status::NonAuthoritativeInformation Informazioni non autorevoli.
204 Status::NoContent Nessun contenuto nella risposta.
205 Status::ResetContent Resetta l'input client.
206 Status::PartialContent Contenuto parziale (range).
207 Status::MultiStatus Multi-status (WebDAV).
208 Status::AlreadyReported Già riportato (WebDAV).
226 Status::ImUsed Instance-manipulations (HTTP Delta encoding).
300 Status::MultipleChoices Scelte multiple.
301 Status::MovedPermanently Risorsa spostata permanentemente.
302 Status::Found Risorsa trovata (redirect temporaneo).
303 Status::SeeOther Vedi altra risorsa (GET).
304 Status::NotModified Non modificato (cache).
305 Status::UseProxy Usare il proxy indicato (deprecato).
307 Status::TemporaryRedirect Redirect temporaneo (mantiene metodo).
308 Status::PermanentRedirect Redirect permanente (mantiene metodo).
400 Status::BadRequest Richiesta malformata.
401 Status::Unauthorized Richiesta non autenticata.
402 Status::PaymentRequired Payment Required (riservato).
403 Status::Forbidden Accesso negato.
404 Status::NotFound Risorsa non trovata.
405 Status::MethodNotAllowed Metodo HTTP non consentito.
406 Status::NotAcceptable Non accettabile (Accept header).
407 Status::ProxyAuthenticationRequired Autenticazione proxy richiesta.
408 Status::RequestTimeout Timeout della richiesta.
409 Status::Conflict Conflitto nello stato della risorsa.
410 Status::Gone Risorsa rimossa definitivamente.
411 Status::LengthRequired Content-Length richiesto.
412 Status::PreconditionFailed Precondizione fallita.
413 Status::PayloadTooLarge Payload troppo grande.
414 Status::UriTooLong URI troppo lungo.
415 Status::UnsupportedMediaType Media type non supportato.
416 Status::RangeNotSatisfiable Range non soddisfacibile.
417 Status::ExpectationFailed Expectation header non soddisfatta.
418 Status::ImATeapot I'm a teapot (RFC 2324, easter egg).
421 Status::MisdirectedRequest Richiesta mal indirizzata.
422 Status::UnprocessableEntity Entità non processabile (WebDAV).
423 Status::Locked Risorsa bloccata (WebDAV).
424 Status::FailedDependency Dipendenza fallita (WebDAV).
425 Status::TooEarly Too Early (riesecuzione non sicura).
426 Status::UpgradeRequired Upgrade del protocollo richiesto.
428 Status::PreconditionRequired Precondizione richiesta.
429 Status::TooManyRequests Troppe richieste (rate limit).
431 Status::RequestHeaderFieldsTooLarge Campi header troppo grandi.
451 Status::UnavailableForLegalReasons Non disponibile per motivi legali.
500 Status::InternalServerError Errore interno del server.
501 Status::NotImplemented Non implementato.
502 Status::BadGateway Bad gateway.
503 Status::ServiceUnavailable Servizio non disponibile.
504 Status::GatewayTimeout Gateway timeout.
505 Status::HttpVersionNotSupported Versione HTTP non supportata.
506 Status::VariantAlsoNegotiates Variant Also Negotiates.
507 Status::InsufficientStorage Insufficient Storage (WebDAV).
508 Status::LoopDetected Loop detected (WebDAV).
510 Status::NotExtended Not Extended.
511 Status::NetworkAuthenticationRequired

Servire file statici

Esistono almeno 2 modi pr servire file statici. Io ho usato il seguente:

use rocket::fs::NamedFile;
use std::path::{Path, PathBuf};
 
#[get("/static/<file..>")]
async fn files(file: PathBuf) -> Option<NamedFile> {
    let path = Path::new("static/").join(file);
    NamedFile::open(path).await.ok()
}

Templates

Di seguito alcune prove con motori di templates.

Minijinjia

use minijinja::{Environment, context};
 
//Passo al template 2 variabili. Una str e un vec. 
 
#[get("/template")]
fn template() -> Custom<RawHtml<String>> {
    let name = "Fabio Di Matteo";
    let html = include_str!("../views/home.html"); //includo la stringa a cmpile time
    let list = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
 
    //instruisco minijinja
    let mut env = Environment::new();
    env.add_template("hello", html).unwrap();
    let tmpl = env.get_template("hello").unwrap();
    let res = tmpl.render(context!(name => name, list=>list)).unwrap();
 
    Custom(Status::Accepted, RawHtml(res))
}

ed ecco il template html

<!DOCTYPE html>
<html>
 
<head>
	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
	<title>Hello</title>
</head>
 
<body>
	<h1>{{name}}</h1>
	<p>Lista passata da Rust (vec[]) :</p>
	<ul>
		{% for e in list %}
		{% if e == 5 %}
		<hr>
		{% endif %}
		<li>{{e}}</li>
		{% endfor %}
	</ul>
</body>
 
</html>

Rendiamo tutto piu' semplice con macro e funzioni

Per evitare di scrivere ogni volta tutto quel codice ecco una macro per inglobare un template dentro l'eseguibile:

use minijinja::{Environment, Value};
use std::error::Error;
use std::fs;
 
#[macro_export]
macro_rules! template_embed {
    ($file_name:expr, $ctx:expr) => {{
        let text = include_str!($file_name);
        let mut env = Environment::new();
        env.add_template($file_name, text).unwrap();
        let tmpl = env.get_template($file_name).unwrap();
        let res = tmpl.render($ctx).unwrap();
        res
    }};
}
 
pub fn template_load(file_name: &str, ctx: Value) -> Result<String, Box<dyn Error>> {
    let text: String;
    text = fs::read_to_string(file_name)?;
 
    let text_clone = text.clone();
    let mut env = Environment::new();
    env.add_template(file_name, text_clone.as_str())?;
    let tmpl = env.get_template(file_name)?;
    let res = tmpl.render(ctx)?;
    Ok(res)
}

Includere template nell'eseguibile:

#[get("/template")]
fn template() -> Custom<RawHtml<String>> {
    let res = template_embed!("views/home.html", context!(title => "A beatifull title!"));
    Custom(Status::Accepted, RawHtml(res))
 
}

Caricarlo a caldo:

#[get("/template")]
fn template() -> Custom<RawHtml<String>> {
 
     let res =
         templates::template_load("views/home.html", context!(title => "A caldo!"));
 
    Custom(
        Status::Accepted,
        RawHtml(res.unwrap_or_else(|_| "Error on load template file!".to_string())),
    )
}
use rocket::http::Cookie;
use rocket::http::CookieJar;
 
#[get("/get_cookie")]
fn get_cookie(cookies: &CookieJar<'_>) -> Custom<RawHtml<String>> {
 
    match cookies.get("val") {
        Some(c) => {
            let res = format!("<h3>Valore del cookie: {}</h3>", c.value()).to_string();
            Custom(Status::Accepted, RawHtml(res))
        }
        None => {
            let res = "<h3>Cookie non impostato!</h3>".to_string();
            Custom(Status::Accepted, RawHtml(res))
        }
    }
}
 
#[get("/set_cookie")]
fn set_cookie(cookies: &CookieJar<'_>) -> Redirect {
    cookies.add(Cookie::new("val", "mio valore"));
    Redirect::to(uri!(get_cookie))
}
 
#[get("/del_cookie")]
fn del_cookie(cookies: &CookieJar<'_>) -> Redirect {
    cookies.remove("val");
    Redirect::to(uri!(get_cookie))
}

Aggiungere a Cargo.toml le dipendenze:

rocket = { version = "0.5.1", features = ["secrets"] }

Ecco il codice per usare i cookie private:

#[get("/get_cookie_private")]
fn get_cookie_private(cookies: &CookieJar<'_>) -> Custom<RawHtml<String>> {
    match cookies.get_private("val_private") {
        Some(c) => {
            let res = format!("<h3>Valore del cookie: {}</h3>", c.value()).to_string();
            Custom(Status::Accepted, RawHtml(res))
        }
        None => {
            let res = "<h3>Cookie non impostato!</h3>".to_string();
            Custom(Status::Accepted, RawHtml(res))
        }
    }
}
 
#[get("/set_cookie_private")]
fn set_cookie_private(cookies: &CookieJar<'_>) -> Redirect {
    cookies.add_private(Cookie::new("val_private", "mio valore"));
    Redirect::to(uri!(get_cookie_private))
}
 
#[get("/del_cookie_private")]
fn del_cookie_private(cookies: &CookieJar<'_>) -> Redirect {
    cookies.remove("val_private");
    Redirect::to(uri!(get_cookie_private))
}

Implementare le sessioni

Work in progress. Non usare in produzione

Sto provando ad implementare un meccanismo di sessioni con db in memory Valkey . In pratica nel db salvo una struttora del genere:

123456  '{"id":"1","user":"fabio"}'

Dove il nome della chiave è il valore del cookie (in questo caso : 123456) . E il valore della chiave è un json.

Il cookie potrebbe essere generato lato client in javascript e inviato in una richiesta. A livello di sicurezza non importa, in quanto il cookie memorizza soltanto id della sessione. Evitare cmq il furto del cookie sarebbe la cosa migliore da fare. Mitigando anche con la scadenza dello stesso.

Cargo.toml

...
redis = "1.2.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

Le firme delle funzioni commentate

fn get_session(
    cookies: &CookieJar<'_>, // cookie Rocket
    cookie_name: String,     //valkey key
    val: String,             // json key
) -> Result<String, Box<dyn Error>> 
fn set_session(
    cookies: &CookieJar<'_>, //cookie Rocket
    cookie_name: String, // valkey key
    val: String,         // json key
    val_elem: String,    // json key val
) -> Result<String, Box<dyn Error>> 

Le funzioni per intero sono disponibili a questo indirizzo, incluse in una appa base minimale: rocket_base_app

Come usare le funzioni che gestiscono la sessione. Come contenuto del cookie uso “123456” ma potrei generare un cookie con uuid da javascript(lato client) e mandarlo nella successiva richiesta. Un esempio? my_session_app=1234dfs243dfs298984

#[get("/page_get_session")]
fn page_get_session(cookies: &CookieJar<'_>) -> Custom<RawHtml<String>> {
    cookies.add(Cookie::new("session_id", "123456"));
 
    let user_name: String;
    //prendo la variabile user contenuta sottoforma di json nel db valkey
    match get_session(cookies, "session_id".to_string(), "user".to_string()) {
        Ok(u) => {
            user_name = u.to_string();
            return Custom(
                Status::Accepted,
                RawHtml(
                    format!(
                        "<h3>Variabile di sessione letta correttamente: {}</h3>",
                        user_name
                    )
                    .to_string(),
                ),
            );
        }
        Err(e) => {
            return Custom(
                Status::Accepted,
                RawHtml(
                    format!(
                        "<h3>Errore nel leggere la variabile di sessione: {}</h3>",
                        e
                    )
                    .to_string(),
                ),
            );
        }
    }
}
 
#[get("/page_set_session")]
fn page_set_session(cookies: &CookieJar<'_>) -> Custom<RawHtml<String>> {
    cookies.add(Cookie::new("session_id", "123456"));
    match set_session(
        cookies,
        "session_id".to_string(),
        "user".to_string(),
        "Chester Di Matteo".to_string(),
    ) {
        Ok(_) => Custom(
            Status::Accepted,
            RawHtml("<h3>Sessione aggiornata con successo!</h3>".to_string()),
        ),
        Err(e) => Custom(
            Status::InternalServerError,
            RawHtml(
                format!(
                    "<h3>Problemi nel settare la variabile di sessionei: {}</h3>",
                    e
                )
                .to_string(),
            ),
        ),
    }
}

Codice piu' compatto per l'utilizzo di get_session(), set_session()

#[get("/easy_get_session")]
fn easy_get_session(cookies: &CookieJar<'_>) -> Custom<RawHtml<String>> {
    cookies.add(Cookie::new("session_id", "123456"));
    let val =
        get_session(cookies, "session_id".to_string(), "user".to_string()).unwrap_or_else(|e| {
            eprintln!("Problemi nel leggere la sessione: {}", e);
            "".to_string()
        });
    Custom(
        Status::Accepted,
        RawHtml(format!("<h3>Variabile: {}</h3>", val).to_string()),
    )
}

Settare scadenza sessioni

Le sessioni fin qui create sono permanenti sul db valkey. Per settare la scadenza della sessione sul db (non sul cookie) possiamo usare la seguente funzione:

fn set_expire_session(session_name: &str, s: i64) -> Result<bool, Box<dyn Error>> {
    let client = redis::Client::open("redis://127.0.0.1/")?;
    let mut conn = client.get_connection()?;
    let ok: bool = conn.expire(session_name, s)?;
    Ok(ok)
}

Dove session_name è il nome della chiave valkey(che nel nostro caso è anche il valore del cookie[valore, non nome cookie]).

Utilizzo funzione

if let Ok(_) = set_expire_session("123456", 10) {
   println!("Expire sessione settato correttamente.")
} else {
  eprintln!("Problemi nel settare la scadenza della sessione!")
}
programmazione/rust/rocket/appunti.txt · Ultima modifica: 18/06/2026 17:04 da Fabio Di Matteo