Diese Kunst meistern

Warum dieses Framework entwickelt wurde

Als Anfänger hatte ich Schwierigkeiten, bestehende Frameworks wie actix-web und Rocket zu verstehen. Als ich versuchte, einen früheren Go-Webdienst in Rust neu zu schreiben, wirkte jedes Framework komplexer als die verfügbaren Go-Lösungen. Angesichts der ohnehin steilen Lernkurve von Rust – warum sollten Webframeworks noch komplizierter sein?

Als Tokio das Axum-Framework vorstellte, war ich begeistert und dachte, ich könnte endlich die Wartung meines eigenen Webframeworks einstellen. Doch bald merkte ich: Obwohl Axum einfach aussah, erforderte es ausgefeilte Typakrobatik und generische Definitionen. Selbst eine einfache Middleware zu entwickeln, verlangte tiefgehende Rust-Kenntnisse und das Schreiben von viel undurchsichtigem Boilerplate-Code.

Daher entschied ich mich, mein einzigartiges Webframework weiterzuentwickeln – eines, das intuitiv, funktionsreich und anfängerfreundlich ist.

Ist Salvo das Richtige für Sie?

Salvo ist einfach, doch umfassend und leistungsstark – wohl das stärkste im Rust-Ökosystem. Trotz seiner Fähigkeiten bleibt es einfach zu erlernen und zu nutzen, sodass Sie sich keine „Selbstkastrations“-Schmerzen auferlegen müssen.

  • Ideal für Rust-Anfänger: CRUD-Operationen sind alltäglich, und mit Salvo fühlen sich solche Aufgaben so unkompliziert an wie mit Frameworks in anderen Sprachen (z. B. Express, Koa, Gin, Flask). In mancher Hinsicht ist Salvo sogar abstrakter und prägnanter.

  • Geeignet für den Produktiveinsatz: Wenn Sie Rust in der Produktion für robuste, hochperformante Server einsetzen möchten, ist Salvo die richtige Wahl. Obwohl noch nicht in Version 1.0, haben seine Kernfunktionen jahrelange Iterationen durchlaufen, was Stabilität und zeitnahe Problemlösung gewährleistet.

  • Perfekt für Sie, besonders wenn Ihre Haare dünner werden und täglich ausfallen.

Einfachheit erreichen

Hyper übernimmt viele Low-Level-Implementierungen und bildet damit eine zuverlässige Grundlage für allgemeine Anforderungen. Salvo folgt diesem Ansatz und bietet ein leistungsfähiges und flexibles Routing-System sowie wesentliche Funktionen wie Acme, OpenAPI und JWT-Authentifizierung.

Salvo vereint Handler und Middleware: Middleware ist ein Handler. Beide werden über hoop am Router angehängt. Im Wesentlichen verarbeiten sie Request und können Daten in Response schreiben. Ein Handler empfängt drei Parameter: Request, Depot (für temporäre Daten während der Request-Verarbeitung) und Response.

Der Einfachheit halber können Sie unnötige Parameter weglassen oder deren Reihenfolge ignorieren.

use salvo::prelude::*;

#[handler]
async fn hello_world(_req: &mut Request, _depot: &mut Depot, res: &mut Response) {
    res.render("Hello world");
}
#[handler]
async fn hello_world(res: &mut Response) {
    res.render("Hello world");
}

Die Routing-API ist außergewöhnlich einfach und dennoch leistungsstark. Für typische Anwendungsfälle müssen Sie sich nur auf den Typ Router konzentrieren.

Darüber hinaus kann Salvo automatisch OpenAPI-Dokumentation generieren, Parameter extrahieren, Fehler elegant behandeln und benutzerfreundliche Meldungen zurückgeben, sofern eine Struktur die relevanten Traits implementiert. Dadurch wird das Schreiben von Handlern so intuitiv wie das Schreiben gewöhnlicher Funktionen. Diese Funktionen werden wir später detailliert erkunden. Hier ein Beispiel:

#[endpoint(tags("message_logs"))]
pub async fn create_message_log_handler(
    input: JsonBody<CreateOrUpdateMessageLog>,
    depot: &mut Depot,
) -> APPResult<Json<MessageLog>> {
    let db = utils::get_db(depot)?;
    let log = create_message_log(&input, db).await?;
    Ok(Json(log))
}

In diesem Beispiel parst JsonBody<CreateOrUpdateMessageLog> automatisch JSON aus dem Request-Body in den Typ CreateOrUpdateMessageLog (unterstützt mehrere Datenquellen und verschachtelte Typen). Das #[endpoint]-Attribut generiert OpenAPI-Dokumentation für diesen Endpunkt und vereinfacht Parameterextraktion und Fehlerbehandlung.

Routing-System

Ich glaube, Salvos Routing-System sticht hervor. Router können flach oder baumartig sein und unterscheiden zwischen Geschäftslogikbäumen und Zugriffsverzeichnisbäumen. Der Geschäftslogikbaum organisiert Router nach geschäftlichen Anforderungen, die nicht unbedingt mit dem Zugriffsverzeichnisbaum übereinstimmen.

Typischerweise werden Routen so geschrieben:

Router::new().path("articles").get(list_articles).post(create_article);
Router::new()
    .path("articles/{id}")
    .get(show_article)
    .patch(edit_article)
    .delete(delete_article);

Oft erfordern das Anzeigen und Auflisten von Artikeln keine Benutzeranmeldung, während das Erstellen, Bearbeiten oder Löschen von Artikeln sehr wohl eine Anmeldung voraussetzt. Salvos verschachteltes Routing-System bewältigt dies elegant. Wir können öffentliche Routen zusammenfassen:

Router::new()
    .path("articles")
    .get(list_articles)
    .push(Router::new().path("{id}").get(show_article));

Anschließend gruppieren wir geschützte Routen mit Middleware zur Authentifizierung:

Router::new()
    .path("articles")
    .hoop(auth_check)
    .post(create_article)
    .push(Router::new().path("{id}").patch(edit_article).delete(delete_article));

Obwohl beide Router denselben path("articles") haben, können sie demselben übergeordneten Router hinzugefügt werden, was zu folgendem Ergebnis führt:

Router::new()
    .push(
        Router::new()
            .path("articles")
            .get(list_articles)
            .push(Router::new().path("{id}").get(show_article)),
    )
    .push(
        Router::new()
            .path("articles")
            .hoop(auth_check)
            .post(create_article)
            .push(Router::new().path("{id}").patch(edit_article).delete(delete_article)),
    );

{id} matcht ein Pfadsegment. Typischerweise ist eine Artikel-id numerisch, daher können wir den Match mit einem Regex einschränken: r"{id:/\d+/}".