E-Mails versenden
Willkommensnachrichten, Passwort-Resets, Belege, Alerts — die meisten Apps versenden
transaktionale E-Mails. Rustango gibt dir ein Mailer-Trait mit austauschbaren
Backends (Konsole für die Entwicklung, SMTP für die Produktion, ein In-Memory-Recorder für Tests),
einen flüssigen Email-Builder mit Schutz vor Header-Injection und
Template-Rendering. Schreibe mailer.send(&email) einmal; wechsle vom Drucken
in dein Terminal zu echtem SMTP mit einer einzeiligen Änderung — wie Djangos E-Mail-Framework.
Neu bei einem Begriff hier? transaktionale E-Mail, SMTP, Mailer-Backend — siehe das Glossar.
Quelle:
rustango::email(Mailer,ConsoleMailer,InMemoryMailer,NullMailer,SmtpMailer,BoxedMailer,send_mail,MailError) — hinter dememail-smtp-Feature.Lauffähige Version: Jedes Snippet ist kopiert aus
email_doc.rs(cargo test -p rustango --test email_doc); die Send-Helfer und Anhänge werden vonemail_send_helpers.rsundemail_attachments.rsselbst erprobt.
Inhaltsverzeichnis
- Schritt 1 — Eine E-Mail bauen
- Schritt 2 — Einen Mailer wählen
- Schritt 3 — Sie versenden
- Validierung und Schutz vor Header-Injection
- E-Mails testen
- Templates
- Sie außerhalb des Requests versenden
- Referenz
- Siehe auch
Schritt 1 — Eine E-Mail bauen
Email ist ein flüssiger Builder. Setze Empfänger, Betreff und einen Text- und/oder HTML-Body:
use rustango::email::Email;
let email = Email::new()
.to("[email protected]")
.from("[email protected]")
.subject("Welcome")
.body("Thanks for signing up.") // plain-text part
.html_body("<p>Thanks for signing up.</p>"); // optional HTML part
.cc(...), .reply_to(...) und Anhänge sind ebenfalls verfügbar.
Schritt 2 — Einen Mailer wählen
Jedes Backend implementiert Mailer, sodass dein Code den konkreten Typ nie benennt —
halte ein BoxedMailer (Arc<dyn Mailer>):
| Backend | Feature | Verwenden für |
|---|---|---|
ConsoleMailer | email | dev — druckt die Nachricht auf stdout |
SmtpMailer | email-smtp | Produktion — echte Zustellung über SMTP |
InMemoryMailer | email | Tests — zeichnet Nachrichten auf, sendet nichts |
FileMailer | email | dev/CI — schreibt jede Nachricht in eine Datei |
NullMailer | email | E-Mail vollständig deaktivieren |
Baue ihn aus der Konfiguration, damit er sich je Umgebung unterscheidet (ConsoleMailer
lokal, SmtpMailer in prod) über email::from_settings(&settings.email).
Schritt 3 — Sie versenden
Email::send nimmt jedes &dyn Mailer:
email.send(&mailer).await?;
Für einen schnellen Einzelfall überspringt send_mail den Builder:
use rustango::email::send_mail;
send_mail(
&mailer,
"Your report is ready", // subject
"Download it from your dashboard.", // body
Some("[email protected]"), // from (or None for the default)
&["[email protected]", "[email protected]"], // recipients
).await?;
send_many versendet einen Batch in einem Aufruf.
Validierung und Schutz vor Header-Injection
Email::validate() läuft vor dem Versand (und du kannst es selbst aufrufen). Es
lehnt unvollständige Nachrichten ab und verteidigt gegen Header-Injection — ein in einen
Header eingeschmuggelter Zeilenumbruch ist die Art, wie Angreifer ein verstecktes Bcc hinzufügen:
// Missing recipients or an empty subject → MailError::InvalidMessage
Email::new().subject("hi").validate()?; // Err: no recipients
// A CRLF in any header field → MailError::BadHeader (Django's BadHeaderError)
Email::new()
.to("[email protected]")
.subject("Hello\r\nBcc: [email protected]") // injection attempt
.body("x")
.validate()?; // Err: BadHeader
Beide werden im zugrunde liegenden Test verifiziert.
E-Mails testen
Verwende InMemoryMailer — es zeichnet jede Nachricht auf, statt zu senden, sodass Tests
darauf prüfen, was hinausgegangen wäre, ohne Netzwerk:
use rustango::email::InMemoryMailer;
let mailer = InMemoryMailer::new();
welcome_flow(&mailer).await?; // your code under test
let sent = mailer.sent(); // Vec<Email>
assert_eq!(sent.len(), 1);
assert_eq!(sent[0].to, vec!["[email protected]".to_string()]);
assert_eq!(sent[0].subject, "Welcome");
Templates
Für alles jenseits einer Textzeile rendere den Body aus einem Tera-Template,
statt HTML inline zu setzen. Der EmailRenderer des email_templates-Features
folgt einer name.subject.txt / name.txt / name.html-Konvention — ein Template-Satz
produziert den Betreff, den Klartextteil und den HTML-Teil zusammen, sodass die
drei nie auseinanderdriften. Das Mailable-Trait verpackt „ein Ding, das weiß, wie es sich
selbst in eine Email verwandelt" für wiederverwendbare Nachrichten.
Sie außerhalb des Requests versenden
E-Mail inline zu versenden lässt den Nutzer auf deinen SMTP-Server warten und koppelt die Antwort an dessen Verfügbarkeit. Versende sie stattdessen aus einem Hintergrundjob — der Handler kehrt sofort zurück und ein Worker stellt sie zu (mit Retries, falls SMTP ausgefallen ist):
// in the handler: enqueue, don't send inline
queue.dispatch(&SendWelcomeEmail { user_id }).await?;
// the job (see the Background jobs guide):
async fn run(&self) -> Result<(), JobError> {
let email = Email::new().to(/* ... */).subject("Welcome").body("...");
email.send(&*mailer).await.map_err(|e| JobError::Retryable(e.to_string()))?;
Ok(())
}
Das email_jobs-Feature verdrahtet dies für dich.
Referenz
Email-Builder: to · cc · from · reply_to · subject · body ·
html_body · Anhänge · validate() · send(&mailer).
Helfer: send_mail(mailer, subject, body, from, &recipients) ·
send_many(mailer, &emails) · from_settings(&EmailSettings).
MailError: InvalidMessage (unvollständig) · BadHeader (CRLF-Injection) ·
Transport (Backend-/Zustellungsfehler).
Siehe auch
- Hintergrundjobs — E-Mail außerhalb des Requests mit Retries zustellen.
- Konto-Abläufe — Passwort-Reset- / Verifizierungs- / Magic-Link-E-Mails, die darauf aufbauen.
- HTML-Views — die Tera-Engine, die auch E-Mail-Templates verwenden.
- Caching — dasselbe Muster „Backend-austauschen" per Trait.
