Nahtloses serverseitiges Templating in Rust-Webanwendungen mit Askama und Tera
Lukas Schneider
DevOps Engineer · Leapcell

Einleitung
Die Erstellung dynamischer Webanwendungen erfordert oft die Generierung von HTML-Inhalten auf dem Server und deren Übermittlung an den Client. Dieser Prozess, bekannt als serverseitiges Rendering (SSR), ist aus verschiedenen Gründen entscheidend, darunter die Leistung beim erstmaligen Laden der Seite, SEO und die Bereitstellung eines Fallbacks für Benutzer mit deaktiviertem JavaScript. Im Rust-Ökosystem bieten Frameworks wie Actix Web oder Warp zwar exzellente Grundlagen für den Aufbau robuster Webdienste, geben aber nicht von sich aus vor, wie dynamisches HTML gerendert wird. Hier kommen spezielle Templating-Engines ins Spiel, die eine leistungsstarke Möglichkeit bieten, Daten in vordefinierte HTML-Strukturen einzufügen. Dieser Artikel befasst sich mit zwei hoch angesehenen Templating-Engines in Rust: Askama und Tera, und zeigt, wie sie effizientes und ausdrucksstarkes serverseitiges Rendering in Ihren Rust-Webanwendungen ermöglichen.
Serverseitiges Templating in Rust verstehen
Bevor wir uns mit den Besonderheiten von Askama und Tera befassen, klären wir einige Kernkonzepte im Zusammenhang mit serverseitigem Templating.
Templating-Engine: Eine Templating-Engine ist eine Softwarekomponente, die es Ihnen ermöglicht, statische Vorlagendateien (oft HTML mit speziellen Platzhaltern oder Logik) mit dynamischen Daten zu kombinieren, um ein finales Dokument zu erstellen, typischerweise eine HTML-Seite.
Server-Side Rendering (SSR): Der Prozess des Renderns von Webseiten auf dem Server und anschließender Übermittlung des vollständig formatierten HTML an den Client. Dies steht im Gegensatz zum Client-Side Rendering (CSR), bei dem eine minimale HTML-Seite gesendet wird und JavaScript auf dem Client Daten abruft und die Benutzeroberfläche erstellt.
Kontext/Daten: Die dynamischen Informationen, die von Ihrer Rust-Anwendung an die Templating-Engine übergeben werden. Dies geschieht normalerweise in Form von Strukturen oder Maps, die die Werte enthalten, die in die Vorlage eingefügt werden sollen.
Vorlagensprache: Die spezifische Syntax und die Regeln, die innerhalb der Vorlagendateien verwendet werden (z. B. {{ variable }}
, {% for item in items %}
), die die Templating-Engine versteht und verarbeitet.
Der Hauptvorteil der Verwendung einer Templating-Engine ist die klare Trennung der Verantwortlichkeiten: Ihr Rust-Code kümmert sich um die Geschäftslogik und den Datenabruf, während Ihre Vorlagendateien sich ausschließlich auf die Darstellungsschicht konzentrieren. Diese Trennung macht Ihre Codebasis wartbarer, lesbarer und für Front-End-Entwickler einfacher zu handhaben.
Askama: Eine typsichere und zur Kompilierzeit geprüfte Templating-Engine
Askama ist eine beliebte Templating-Engine in Rust, die für ihre Kompilierzeitprüfung und Leistung bekannt ist. Sie nutzt das Makrosystem von Rust, um Vorlagencode zur Kompilierzeit zu generieren, was zu einer hervorragenden Leistung und frühen Erkennung von Vorlagenfehlern führt.
Implementierung mit Askama
Askama verwendet eine Jinja-ähnliche Syntax, die vielen Entwicklern vertraut ist. Lassen Sie uns ein einfaches Beispiel für die Verwendung von Askama mit einer Actix Web-Anwendung durchgehen.
Fügen Sie zunächst Askama und Actix Web zu Ihrer Cargo.toml
hinzu:
[dependencies] actix-web = "4" askama = "0.12"
Definieren Sie als Nächstes Ihre Vorlage und die Datenstruktur, die sie füllen wird. Askama verwendet ein Derive-Makro, um eine Struktur einer Vorlagendatei zuzuordnen.
// src/templates.rs use askama::Template; #[derive(Template)] #[template(path = "hello.html")] pub struct HelloTemplate<'a> { pub name: &'a str, pub items: Vec<&'a str>, }
Erstellen Sie nun die Vorlagendatei hello.html
in einem Verzeichnis namens templates
am Stammverzeichnis Ihres Projekts:
<!-- templates/hello.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Hello from Askama</title> </head> <body> <h1>Hello, {{ name }}!</h1> <p>Here are some of your favorite things:</p> <ul> {% for item in items %} <li>{{ item }}</li> {% endfor %} </ul> </body> </html>
Integrieren Sie dies schließlich in Ihre Actix Web-Anwendung:
// src/main.rs use actix_web::{web, App, HttpResponse, HttpServer, Responder}; use askama::Template; // Bring Template trait into scope use crate::templates::HelloTemplate; mod templates; // Declare the module where your Askama templates are defined async fn greet() -> impl Responder { let template = HelloTemplate { name: "Rustacean", items: vec!["🦀", "💻", "🚀"], }; HttpResponse::Ok().body(template.render().unwrap()) } #[actix_web::main] async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new().route("/", web::get().to(greet)) }) .bind(("127.0.0.1", 8080))? .run() .await }
Wenn Sie diese Anwendung ausführen und http://127.0.0.1:8080
aufrufen, sehen Sie eine HTML-Seite, die mit "Hello, Rustacean!" und einer Liste von Elementen gerendert wird, die dynamisch aus Ihrem Rust-Code eingefügt wurden. Das Schöne an Askama ist hier, dass der Rust-Compiler Tippfehler bei {{ name }}
(z. B. {{ namme }}
) erkennt und so Laufzeitfehler verhindert.
Tera: Eine funktionsreiche und flexible Templating-Engine
Tera ist eine weitere robuste Templating-Engine für Rust, die weitgehend von Jinja2 und Django-Templates inspiriert ist. Sie bietet eine Vielzahl von Funktionen, darunter Template-Vererbung, Makros, Filter und benutzerdefinierte Funktionen, was sie für komplexe Webanwendungen äußerst vielseitig macht. Im Gegensatz zum Kompilierzeitansatz von Askama verarbeitet Tera Templates zur Laufzeit, was ihm mehr Flexibilität in bestimmten Szenarien gibt, z. B. beim Laden von Templates aus verschiedenen Quellen oder beim Hot-Reloading während der Entwicklung.
Implementierung mit Tera
Passen wir unser vorheriges Beispiel an, um Tera zu verwenden.
Fügen Sie zunächst Tera und Actix Web zu Ihrer Cargo.toml
hinzu:
[dependencies] actix-web = "4" tera = "1" serde = { version = "1", features = ["derive"] } serde_json = "1"
Tera arbeitet typischerweise mit serde
zusammen, um Daten in ein Context
-Objekt zu serialisieren.
Richten Sie als Nächstes tera
ein und definieren Sie einen Endpunkt zum Rendern einer Vorlage. Da Tera keine Derive-Makros auf Strukturen verwendet, erstellen Sie explizit einen Context
.
// src/main.rs use actix_web::{web, App, HttpResponse, HttpServer, Responder}; use serde::{Deserialize, Serialize}; use tera::{Context, Tera}; // Struct zur Aufnahme von Daten für die Vorlage #[derive(Serialize, Deserialize)] struct UserData { name: String, items: Vec<String>, } async fn greet_tera(tera: web::Data<Tera>) -> impl Responder { let mut context = Context::new(); let user_data = UserData { name: "Gopher".to_string(), items: vec!["🏝️".to_string(), "☁️".to_string(), "🔬".to_string()>], }; context.insert("user", &user_data); // Tera erwartet eine Map-ähnliche Struktur für Kontextdaten let rendered = tera.render("hello.html", &context).unwrap_or_else(|err| { eprintln!("Tera rendering error: {}", err); "Error rendering template".to_string() }); HttpResponse::Ok() .content_type("text/html") .body(rendered) } #[actix_web::main] async fn main() -> std::io::Result<()> { let tera = match Tera::new("templates/**/*.html") { Ok(t) => t, Err(e) => { eprintln!("Parsing error(s): {}", e); ::std::process::exit(1); } }; HttpServer::new(move || { App::new() .app_data(web::Data::new(tera.clone())) // Tera-Instanz über Worker teilen .route("/tera", web::get().to(greet_tera)) }) .bind(("127.0.0.1", 8081))? .run() .await }
Erstellen Sie die Vorlagendatei hello.html
in einem templates
-Verzeichnis am Stammverzeichnis Ihres Projekts. Beachten Sie, wie wir in Tera auf verschachtelte Daten zugreifen (z. B. user.name
).
<!-- templates/hello.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Hello from Tera</title> </head> <body> <h1>Hello, {{ user.name }}!</h1> <p>Here are some of your favorite things:</p> <ul> {% for item in user.items %} <li>{{ item }}</li> {% endfor %} </ul> </body> </html>
Wenn Sie diese Anwendung ausführen und http://127.0.0.1:8081/tera
aufrufen, sehen Sie eine ähnlich gerenderte Seite, die jedoch von Tera angetrieben wird.
Askama und Tera im Vergleich
Sowohl Askama als auch Tera sind ausgezeichnete Optionen, aber sie sprechen leicht unterschiedliche Vorlieben und Anwendungsfälle an:
- Askama:
- Vorteile: Kompilierzeitprüfung (erkennt Fehler frühzeitig), hervorragende Leistung durch generierten Code, starke Typsicherheit beim Zuordnen von Strukturen zu Vorlagen.
- Nachteile: Weniger flexibel für dynamisches Laden/Neuladen von Vorlagen, Vorlagen werden mit der Anwendung neu kompiliert.
- Am besten für: Anwendungen, bei denen Leistung und Kompilierzeitgarantien oberste Priorität haben und die Vorlagenstruktur relativ stabil ist.
- Tera:
- Vorteile: Reichhaltiger Funktionsumfang (Makros, Vererbung, Filter), Laufzeitladen und Zwischenspeichern von Vorlagen, gut für komplexe Vorlagenhierarchien, Hot-Reloading während der Entwicklung.
- Nachteile: Laufzeitfehler (Tippfehler in Vorlagenvariablen werden erst beim Rendern erkannt), etwas geringere Leistung als Askama (obwohl immer noch sehr schnell).
- Am besten für: Anwendungen, die umfangreiche Vorlagenfunktionen, dynamisches Vorlagenladen benötigen oder bei denen die Entwicklungsflexibilität bezüglich Vorlagen im Vordergrund steht.
Anwendungsfälle für serverseitiges Templating
Serverseitiges Templating mit Engines wie Askama und Tera findet seine Nützlichkeit in verschiedenen Webanwendungs-Mustern:
- Traditionelle Webanwendungen: Für Full-Stack-Anwendungen, bei denen der Großteil der Benutzeroberfläche auf dem Server gerendert wird, um schnelle erste Seitenladezeiten und bessere SEO zu gewährleisten.
- Hybride Anwendungen (SSR + Hydration): Bereitstellung des anfänglichen, vollständig gerenderten HTMLs für eine schnelle erste Darstellung, die dann durch Client-seitiges JavaScript für Interaktivität "hydriert" werden kann.
- E-Mail-Vorlagen: Generierung von reichhaltigen HTML-E-Mails durch dynamisches Einfügen benutzerspezifischer Daten in vordefinierte Layouts.
- Static Site Generators (SSG): Obwohl nicht ihr primärer Fokus, sind Templating-Engines der Kern vieler SSGs und verarbeiten Datendateien und Vorlagen zu statischem HTML.
Fazit
Die Integration von serverseitigem Templating in Ihre Rust-Webanwendungen mit Engines wie Askama und Tera verbessert Ihre Fähigkeit, dynamische, wartbare und robuste Benutzeroberflächen zu erstellen, erheblich. Askama bietet durch Kompilierzeitgarantien unübertroffene Typsicherheit und Leistung, während Tera einen reichhaltigen, flexiblen Funktionsumfang für komplexere Templating-Anforderungen bietet. Indem Sie die Stärken beider verstehen, können Sie das richtige Werkzeug wählen, um dynamische Inhalte nahtlos zu rendern und so Ihre Rust-Webentwicklungserfahrung sowohl leistungsfähig als auch angenehm zu gestalten. Letztendlich ermöglicht Ihnen die Wahl von Askama oder Tera, überzeugende Web-Erlebnisse direkt aus Ihrem Hochleistungs-Rust-Backend zu schaffen.