Eine E-Mail-Adresse in Go zu validieren sind drei Aufgaben, die einen Namen
tragen. Die Tutorials, die Ihnen eine Regex zeigen und dann aufhören, haben
genau eine davon gelöst – und dazu die am wenigsten nützliche. Echte Validierung
ist mehrschichtig: eine günstige Syntaxprüfung, eine DNS-Abfrage und eine
SMTP-Postfachprüfung, das Günstigste zuerst. Gos Standardbibliothek deckt die
ersten beiden sauber mit net/mail und net ab, ganz ohne Abhängigkeiten;
die dritte ist ein Netzwerkproblem, das man besser abgibt. Dieser Leitfaden baut
jede Ebene mit funktionierendem, idiomatischem Go auf und zeigt genau, wo eine
Verifizierungs-API übernimmt.
Die kurze Antwort#
Verwenden Sie net/mail.ParseAddress für die Syntax, net.LookupMX für die
MX-Abfrage und eine Verifizierungs-API für die SMTP-Postfachprüfung – in dieser
Reihenfolge, das Günstigste zuerst, mit Kurzschluss, sobald eine Ebene eine
Entscheidung liefert. Greifen Sie nicht zu net/smtp, um Postfächer von Ihrem
eigenen Server aus zu prüfen: Der ausgehende Port 25 ist auf den meisten Hosts
blockiert, und selbst dort, wo er es nicht ist, hängt die Antwort von der
Reputation der sendenden IP und vom Greylisting-Verhalten ab, das Sie nicht
nachbauen wollen. Jede Ebene sortiert Adressen günstiger aus als die
vorherige; nur die letzte kann eine Adresse als gültig bestätigen.
Ebene 1: Syntax mit net/mail#
Go braucht hier keine selbst gebaute Regex. Das net/mail-Paket der
Standardbibliothek parst Adressen nach RFC 5322, und ParseAddress gibt bei
allem Fehlerhaften einen Fehler ungleich nil zurück – genau das
Ja/Nein-Signal, das ein Syntax-Gate braucht:
package main
import "net/mail"
func isValidSyntax(email string) bool {
if len(email) > 320 {
return false
}
addr, err := mail.ParseAddress(email)
return err == nil && addr.Address == email
}
Der Vergleich addr.Address == email ist der Teil, den die meisten Beispiele
übersehen. ParseAddress ist darauf ausgelegt, eine vollständige Mailbox-Zeile
zu lesen, und akzeptiert daher bereitwillig einen Anzeigenamen:
mail.ParseAddress("Jane Doe <jane@example.com>") gibt keinen Fehler zurück,
wobei addr.Address auf jane@example.com gesetzt ist. Für ein Anmeldefeld
wollen Sie die reine Adresse und nichts sonst, also vergleichen Sie die
geparste Address mit der Eingabe und weisen Sie alles zurück, was einen Namen,
spitze Klammern oder nachgestellte Leerzeichen mitbrachte:
isValidSyntax("jane@example.com") // true
isValidSyntax("Jane Doe <jane@example.com>") // false — not a bare address
isValidSyntax("not-an-email") // false — ParseAddress errors
isValidSyntax("a@@b.com") // false
Ein Bestehen bedeutet hier „prüfenswert“, nicht „gültig“. Es sagt Ihnen, dass
die Zeichenkette wie eine Adresse geformt ist; über die Frage, ob Mail
ankommt, sagt es nichts aus. Das ist dieselbe Wand, gegen die jede
Regex-basierte E-Mail-Prüfung
läuft, und net/mail steht auf
derselben Seite davon – es ist ein Parser, kein Zustellbarkeitsorakel. Jede
Ebene darunter setzt voraus, dass die Syntax bereits in Ordnung ist.
Ebene 2: Kann die Domain überhaupt Mail empfangen?#
Hier macht sich die Standardbibliothek ohne jede Abhängigkeit bezahlt. Eine
Domain, die keine MX-Einträge veröffentlicht, hat keinen Ort, an dem Mail landen
kann, sodass eine einzige Abfrage tote Domains, falsch geschriebene
Firmennamen und erfundene TLDs eliminiert. net.LookupMX löst sie auf:
package main
import "net"
func hasMailRoute(domain string) bool {
mxs, err := net.LookupMX(domain)
if err != nil {
return false
}
return len(mxs) > 0
}
hasMailRoute("gmail.com") // true
hasMailRoute("company-that-folded.com") // false
LookupMX gibt einen Slice von *net.MX zurück, nach Präferenz sortiert, oder
einen *net.DNSError, wenn die Domain nicht aufgelöst werden kann. Jeden Fehler
– oder einen leeren Slice – als „keine Mailroute“ zu behandeln, ist die sichere
Lesart für einen Validator. Wenn Sie Domains berücksichtigen wollen, die Mail
über einen A-Eintrag ohne expliziten MX annehmen (den sogenannten impliziten
MX), greifen Sie auf net.LookupHost(domain) zurück, wenn der MX-Slice leer
zurückkommt. Für die überwältigende Mehrheit echter Adressen ist eine MX-Prüfung
jedoch der richtige Filter.
Eine betriebliche Anmerkung: LookupMX blockiert auf Ihrem DNS-Resolver, sodass
ein langsamer oder nicht erreichbarer Nameserver die Goroutine anhält.
Verwenden Sie in einem Request-Pfad den kontextbewussten Resolver –
(&net.Resolver{}).LookupMX(ctx, domain) – mit einer Deadline, und behandeln
Sie einen Timeout als „später prüfen“, nicht als harte Ablehnung, damit ein
vorübergehender DNS-Aussetzer nie einen echten Kunden abweist.
Ebene 3: Existiert das Postfach tatsächlich?#
Die Ebenen 1 und 2 können eine Adresse nur aussortieren. Eine Domain kann
perfekte MX-Einträge veröffentlichen und dennoch kein Postfach unter der
Adresse haben, die Sie in Händen halten – noreply-9f2x@gmail.com ist
syntaktisch gültig auf einer aktiven Mailroute und war nie ein echtes Postfach.
Ein bestimmtes Postfach zu bestätigen bedeutet die SMTP-Zustellkonversation:
zum Mailhost verbinden, RCPT TO absetzen, die Antwort lesen und trennen, bevor
irgendetwas gesendet wird.
Go liefert die Werkzeuge, es zu versuchen. net/smtp gibt Ihnen smtp.Dial
und einen Client mit den Methoden Mail, Rcpt und Close, und Sie könnten
diesen Handshake im Prinzip selbst steuern. In der Praxis sollten Sie ihn nicht
von Ihrem Anwendungsserver aus ausführen. Die meisten Cloud-Anbieter blockieren
den ausgehenden Port 25 vollständig, sodass der Verbindungsaufbau in der
Produktion einfach abläuft, selbst wenn er auf Ihrem Laptop funktioniert hat.
Wo der Port offen ist, hängt die Antwort von der Reputation der IP ab, von der
aus Sie sich verbinden, und empfangende Server wenden Greylisting und
Rate-Limiting auf unbekannte Absender an – sodass eine naive Prüfung
zurückgestellt, gedrosselt oder auf Sperrlisten gesetzt wird. Es steckt auch
mehr dahinter als ein einziger Roundtrip: Catch-all-Domains akzeptieren jede
Adresse und laufen einem einzelnen RCPT TO zuwider.
Wie E-Mail-Verifizierung funktioniert
geht die gesamte Pipeline durch. Das ist die Ebene, die es wert ist, delegiert
zu werden.
Die vollständige Prüfung mit einer API durchführen#
Die Echtzeit-API von Qualisend durchläuft die gesamte Pipeline – Syntax, DNS
und die SMTP-Postfachprüfung – auf einer reputationsverwalteten Infrastruktur,
die dafür gebaut ist, und liefert ein Urteil zurück. Es ist ein einfacher
JSON-POST, sodass net/http und encoding/json aus der Standardbibliothek
alles sind, was Sie brauchen. Dekodieren Sie den result-Umschlag direkt in
eine Struktur:
package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"os"
)
type VerifyResult struct {
Status string `json:"status"` // deliverable | risky | undeliverable | unknown
Score int `json:"score"`
Reason string `json:"reason"`
SubFlags map[string]bool `json:"sub_flags"`
}
type verifyResponse struct {
Result VerifyResult `json:"result"`
}
func verify(ctx context.Context, email string) (VerifyResult, error) {
body, err := json.Marshal(map[string]string{"email": email})
if err != nil {
return VerifyResult{}, err
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost,
"https://api.qualisend.com/v1/verify", bytes.NewReader(body))
if err != nil {
return VerifyResult{}, err
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("QUALISEND_API_KEY"))
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
return VerifyResult{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return VerifyResult{}, fmt.Errorf("qualisend: unexpected status %s", res.Status)
}
var payload verifyResponse
if err := json.NewDecoder(res.Body).Decode(&payload); err != nil {
return VerifyResult{}, fmt.Errorf("qualisend: decode response: %w", err)
}
return payload.Result, nil
}
Der Endpunkt und der Schlüssel oben sind Platzhalter – lesen Sie Ihren
gescopten Schlüssel aus der Umgebungsvariablen QUALISEND_API_KEY, anstatt ihn
fest einzucodieren, und prüfen Sie die
API-Referenz auf der Entwicklerseite für das genaue Format von
Request und Response. Das result-Objekt trägt einen status von
deliverable, risky, undeliverable oder unknown, einen numerischen
score, einen reason und eine sub_flags-Map (Rollenadresse, Wegwerfadresse,
Freemail-Anbieter, Catch-all und dergleichen) – die Felder, auf die Sie
tatsächlich verzweigen.
Da verify einen context.Context entgegennimmt, besitzt der Aufrufer das
Timeout und die Cancellation. Übergeben Sie eine Deadline, damit eine langsame
Verifizierung nie einen Anmelde-Request aufhängt:
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
result, err := verify(ctx, "jane@example.com")
if err != nil {
log.Printf("verify failed: %v", err)
}
Alles zusammenfügen, um eine E-Mail-Adresse in Go zu validieren#
Das Günstigste zuerst, aufhören, sobald Sie eine Antwort haben. Die beiden lokalen Ebenen kosten nichts und weisen den meisten Unrat sofort ab; der API-Aufruf läuft nur auf Adressen, die sie überstanden haben und den Netzwerk- Roundtrip wert sind:
package main
import (
"context"
"strings"
)
func validateEmail(ctx context.Context, email string) (VerifyResult, error) {
if !isValidSyntax(email) {
return VerifyResult{Status: "undeliverable", Reason: "invalid_email"}, nil
}
domain := email[strings.LastIndex(email, "@")+1:]
if !hasMailRoute(domain) {
return VerifyResult{Status: "undeliverable", Reason: "invalid_domain"}, nil
}
return verify(ctx, email)
}
Verzweigen Sie dann auf den Status, so wie Ihr Produkt es braucht – die Unzustellbaren abweisen, die Zustellbaren durchlassen und pro Feature entscheiden, was mit den Urteilen in der Grauzone geschehen soll:
result, err := validateEmail(ctx, "jane@example.com")
if err != nil {
// network/API failure — fail open or retry, don't block a real user
}
switch result.Status {
case "deliverable":
// accept
case "undeliverable":
// reject at the form
default:
// risky | unknown — flag for review, or allow with a soft warning
}
Diese Reihenfolge ist der ganze Trick, und es ist dieselbe Form, die Sie in den Node.js- und Python-Versionen dieses Leitfadens finden – die Schichtung, nicht die Sprache, macht die Validierung zuverlässig. Siehe wie E-Mail-Verifizierung funktioniert dafür, warum jede Stufe dort sitzt, wo sie sitzt.
Häufig gestellte Fragen#
Reicht net/mail.ParseAddress aus, um eine E-Mail-Adresse in Go zu validieren?#
Für die Syntax: ja – net/mail.ParseAddress ist die richtige Prüfung auf Ebene
eins und eine weitaus bessere Wahl als eine selbst gebaute Regex, da es gegen
RFC 5322 parst und bei fehlerhafter Eingabe einen Fehler zurückgibt. Aber es
validiert die Form, nicht die Zustellbarkeit: Es löst niemals DNS auf und
kontaktiert keinen Mailserver, sodass ein nil-Fehler „sieht aus wie eine
Adresse“ bedeutet, nicht „wird zugestellt“. Vergleichen Sie addr.Address mit
Ihrer Eingabe, um Anzeigenamen abzuweisen, und kombinieren Sie die Prüfung dann
mit einer MX-Abfrage und einer SMTP-Postfachprüfung.
Wie frage ich MX-Einträge in Go ab?#
Verwenden Sie net.LookupMX(domain) aus der Standardbibliothek. Es gibt einen
Slice von *net.MX-Einträgen zurück, nach Präferenz sortiert, oder einen
*net.DNSError, wenn die Domain nicht aufgelöst werden kann. Behandeln Sie
einen Fehler oder einen leeren Slice als „keine Mailroute“, und verwenden Sie in
einem Request-Pfad das kontextbewusste (*net.Resolver).LookupMX(ctx, domain),
damit ein langsamer Nameserver die Goroutine nicht über Ihre Deadline hinaus
blockieren kann.
Kann ich ein Postfach in Go ohne externen Dienst verifizieren?#
Teilweise. net.LookupMX bestätigt, dass die Domain Mail annimmt, was tote
Domains kostenlos aussortiert und nichts über die Standardbibliothek hinaus
benötigt. Das Postfach selbst zu bestätigen erfordert eine SMTP-Konversation,
die Sie mit net/smtp skripten können, aber nicht von Ihrem App-Server aus
ausführen sollten – Port 25 ist weithin blockiert und das Ergebnis hängt von der
Reputation Ihrer sendenden IP ab. Genau diese Ebene übernimmt ein
Verifizierungsdienst; den Unterschied sehen Sie bei jeder Adresse mit dem
kostenlosen E-Mail-Checker.
Sollte ich E-Mails bei der Anmeldung oder beim Bereinigen einer Liste validieren?#
Beides, in unterschiedlicher Tiefe. Führen Sie die Syntaxprüfung und die MX-Abfrage bei der Anmeldung synchron aus – sie sind schnell genug, um den Request zu blockieren und sofortiges Feedback zu geben – und handeln Sie auch dort nach dem unmittelbaren API-Urteil. Reservieren Sie die tiefere, SMTP-bestätigte Arbeit für die Listenbereinigung und Backoffice-Jobs; der Leitfaden zur serverlosen Anmeldung zeigt das Echtzeitmuster von Anfang bis Ende.
Bereit, die SMTP-Ebene zu Ihrem Go-Dienst hinzuzufügen? Die
Qualisend-API-Referenz enthält den /verify-Endpunkt mit
gescopten Schlüsseln und Copy-and-paste-Beispielen, und der
kostenlose E-Mail-Checker lässt Sie zusehen, wie eine
syntaktisch perfekte Adresse als undeliverable zurückkommt, bevor Sie eine
Zeile Code schreiben.