Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Rust: come creare API REST

Rust: come creare API REST dalla gestione delle dipendenze passando per l'avvio delle API e fino al test del codice
Rust: come creare API REST dalla gestione delle dipendenze passando per l'avvio delle API e fino al test del codice
Link copiato negli appunti

Ora è arrivato il momento di creare le API REST con Rust. Il codice presentato il questa lezione completa quanto fatto nella precedente pertanto può essere realizzato come una sorta di sua evoluzione.

Recuperiamo il file mongo_database.rs del codice mentre riscriveremo totalmente il main.rs che qui servirà per avviare le API e completerà la parte di destinazione della migrazione.

Gestione delle dipendenze e avvio delle API REST

Per quanto riguarda le dipendenze, la relativa sezione del file Cargo.toml è stata aggiornata nel seguente modo:

[dependencies]
warp = "0.3.7"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1", features = ["full"] }
mongodb = "3.2.3"
bson = "2.14"

Avvio delle API con Rust

Ecco il codice che avvia le API REST:

// importazione delle librerie
use warp::Filter;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
mod mongo_database;
use mongo_database::{Database, Libro};
#[derive(Debug)]
struct DatabaseError;
impl warp::reject::Reject for DatabaseError {}
// il codice è asincrono
#[tokio::main]
async fn main() {
    // Connessione al database in locale. Verificare con propria installazione
    let db = Database::connect("mongodb://localhost:27017", "biblioteca", "libri")
        .await
        .expect("Impossibile collegarsi al database! Errore...");
    // componente di connessione al database MongoDB
    let db = Arc::new(db);
    // Definiamo la route POST /libri
    let create_libro = warp::path("libri")
        .and(warp::post())
        .and(warp::body::json())
        .and(with_db(db.clone()))
        .and_then(crea_libro_handler);
    println!("Server in ascolto all'indirizzo http://localhost:3030");
    // attivazione del servizio in locale
    warp::serve(create_libro)
        .run(([127, 0, 0, 1], 3030))
        .await;
}
// Gestione delle operazioni sui dati
async fn crea_libro_handler(libro: Libro, db: Arc<Database>) -> Result<impl warp::Reply, warp::Rejection> {
    let libro_db = Libro {
        id: None,
        titolo: libro.titolo,
        autore: libro.autore,
        numero_pagine: libro.numero_pagine,
    };
    match db.crea_libro(libro_db).await {
        Ok(id) => Ok(warp::reply::json(&format!("ID del nuovo documento: {}", id))),
        Err(e) => {
            eprintln!("ERRORE: {}", e);
            Err(warp::reject::custom(DatabaseError))
        }
    }
}
// filtro per il collegamento tra database e API REST
fn with_db(db: Arc<Database>) -> impl Filter<Extract = (Arc<Database>,), Error = std::convert::Infallible> + Clone {
    warp::any().map(move || db.clone())
}

Codice di definizione delle API

Si presti attenzione, per apprezzare Warp, a come sia configurato il codice di definizione delle API:

warp::path("libri")
        .and(warp::post())
        .and(warp::body::json())
        .and(with_db(db.clone()))
        .and_then(crea_libro_handler);

Viene indicato il tratto di percorso dell'URL e successivamente il metodo da usare (POST) per poi definire sia gli aspetti di conversione in JSON e, con le ultime due righe, vengono forniti gli strumenti per poter, rispettivamente, accedere al database e gestire la chiamata con il codice operativo.

L'utilizzo del filtro with_db è una convenzione assai comune per fare in modo che una componente già pronta per l'interazione di un database venga "iniettata" nell'handler di gestione della chiamata ed essere pertanto disponibile per il suo utilizzo. Notiamo infatti che la funzione crea_libro_handler se la trova già disponibile da poter sfruttare.

Test del codice Rust

Una volta avviato il server con un semplice comando cargo run, avremo un servizio in attesa di ricevere dati da salvare nel database MongoDB. Per provarlo, si potranno eseguire operazioni con i client che si preferisce ma noi abbiamo usato il classico curl, tool da riga di comando per l'interazione con servizi in HTTP. Questa la richiesta POST che abbiamo svolto:

curl -X POST http://localhost:3030/libri
	     -H "Content-Type: application/json"
		 -d '{"titolo":
			  "Progettare belle case",
			  "autore": "Andreina Rossi",
			  "numero_pagine": 1000}'

e questo il risultato che otteniamo a conferma del salvataggio dei dati:

ID del nuovo documento: 67fbe06db9eb08dd2d4bbc55

Procedendo ad una veloce prova con il client mongosh, integrato in MongoDB, otteniamo la seguente conferma:

> db.libri.find()
[
  {
    _id: ObjectId('67fbe06db9eb08dd2d4bbc55'),
    titolo: 'Progettare belle case',
    autore: 'Andreina Rossi',
    numero_pagine: 1000
  }
]

La parte "server" della nostra sperimentazione è conclusa quindi. Possiamo pertanto dirigerci alla conclusione con il client MySQL/HTTP della prossima lezione su Rust.

Ti consigliamo anche