Um eine E-Mail-Adresse in C# zu validieren, greifen die meisten Entwickler zu
System.Net.Mail.MailAddress und machen weiter. Das ist ein vertretbarer erster
Schritt — der Typ ist im Framework eingebaut und weist Müll ohne selbst gebaute
Regex zurück —, aber er beantwortet nur die erste der drei Fragen, die echte
Validierung stellt. Ist die Adresse korrekt geformt? Kann ihre Domain Mail
empfangen? Existiert das Postfach tatsächlich? Die .NET-Basisklassenbibliothek
beantwortet die erste Frage sauber, benötigt für die zweite ein bekanntes
NuGet-Paket und überlässt die dritte einem Netzwerkproblem, das man besser
auslagert. Dieser Leitfaden baut E-Mail-Validierung in C# als drei Schichten auf,
jeweils mit idiomatischem async/await-Code, und zeigt genau, wo MailAddress
aufhört.
Die kurze Antwort#
Verwenden Sie MailAddress.TryCreate für die Syntax, das Paket DnsClient.NET für
den MX-Lookup und eine Verifizierungs-API für die SMTP-Postfachprüfung — das
Günstigste zuerst, mit Kurzschluss, sobald eine Schicht entscheidend ist. Öffnen
Sie keine rohen SMTP-Sockets aus Ihrer App, um Postfächer selbst zu prüfen: Der
ausgehende Port 25 ist auf den meisten Hosts blockiert, und die Antwort hängt von
der Reputation der sendenden IP und von Greylisting ab, das Sie nicht neu
implementieren wollen. Jede Schicht schließt Adressen günstiger aus als die
vorige; nur die letzte kann eine Adresse einschließen. Wenn Sie nur ein Verdikt
für eine einzelne Adresse brauchen, fügen Sie sie in den
kostenlosen E-Mail-Checker ein und überspringen Sie den
Code — der Rest dieses Leitfadens dient dazu, die Prüfung in eine App einzubauen.
Die Konzepte hinter jeder Stufe finden Sie unter
was E-Mail-Verifizierung ist.
Schicht 1: Syntax mit MailAddress.TryCreate#
MailAddress ist das richtige Werkzeug für Schicht eins. Bevorzugen Sie die in
.NET 5 hinzugefügte Überladung TryCreate: Sie gibt bei fehlerhafter Eingabe
einen bool zurück, statt eine FormatException zu werfen, sodass Sie darauf
prüfen können, ohne jeden Aufruf in ein try/catch zu verpacken.
using System.Net.Mail;
public static bool IsValidSyntax(string email)
{
return !string.IsNullOrWhiteSpace(email)
&& email.Length <= 320
&& MailAddress.TryCreate(email, out var parsed)
&& parsed.Address == email;
}
IsValidSyntax("jane@example.com"); // true
IsValidSyntax("not-an-email"); // false
IsValidSyntax("Jane <jane@example.com>"); // false
Dieser letzte Vergleich ist wichtig. MailAddress ist ein Mail-Parser, kein
strenger Validator — er akzeptiert bereitwillig Anzeigename-Formen wie
Jane <jane@example.com> und legt den Adressteil über parsed.Address offen.
Wenn Sie parsed.Address mit der rohen Eingabe vergleichen, weisen Sie solche
Formen und versprengte Leerzeichen zurück — genau das, was Sie von einer
Ja/Nein-Syntaxprüfung auf Benutzereingaben erwarten. Der Schutz auf 320 Zeichen
ist doppelt abgesichert: Nichts Längeres kann eine echte Adresse sein, und es ist
günstiger, früh abzulehnen, als eine riesige Zeichenkette an nachgelagerte
Verarbeitung zu übergeben.
Auf .NET Framework oder jedem Ziel älter als .NET 5 existiert TryCreate nicht,
greifen Sie also auf den Konstruktor zurück und fangen Sie die Exception ab:
try { _ = new MailAddress(email); /* syntax ok */ }
catch (FormatException) { /* reject */ }
Wenn Sie bereits Data Annotations auf einem Modell verwenden, erledigt das
System.ComponentModel.DataAnnotations.EmailAddressAttribute ([EmailAddress])
dieselbe Aufgabe deklarativ und ist eine gute Wahl innerhalb der
Modellvalidierung von ASP.NET Core. Was auch immer Sie wählen: Behandeln Sie ein
Bestehen als „lohnt sich, richtig zu prüfen“, niemals als „gültig“. Eine
Syntaxprüfung weiß nichts über DNS oder Postfächer — aus demselben Grund
scheitert Regex-basierte E-Mail-Validierung:
Form und Zustellbarkeit sind unterschiedliche Fragen, die eine ist eine Tatsache
über die Zeichenkette, die andere eine Tatsache über das Internet.
Schicht 2: Kann die Domain Mail empfangen?#
Eine Domain ohne Mail-Route kann für niemanden Mail annehmen, dieser eine Lookup
eliminiert also tote Domains, falsch geschriebene Firmennamen und erfundene TLDs,
bevor Sie das Netzwerk überhaupt richtig berühren. Der Haken: .NET hat keinen
eingebauten MX-Resolver. System.Net.Dns löst A- und AAAA-Einträge auf, aber
kein MX, die idiomatische Antwort ist also DnsClient.NET — ein weit verbreitetes,
gut gepflegtes NuGet-Paket, zu dem die meisten .NET-Projekte greifen, wenn sie DNS
über Host-Lookups hinaus brauchen. Installieren Sie es und fragen Sie den
MX-Eintragstyp ab:
using System.Linq;
using DnsClient; // dotnet add package DnsClient
// LookupClient is thread-safe and caches responses — create one and reuse it.
private static readonly LookupClient Dns = new();
public static async Task<bool> HasMailRouteAsync(string domain)
{
try
{
var response = await Dns.QueryAsync(domain, QueryType.MX);
return response.Answers.MxRecords().Any();
}
catch (DnsResponseException)
{
// No reachable resolver, SERVFAIL, and similar — treat as "check later".
return false;
}
}
await HasMailRouteAsync("gmail.com"); // true
await HasMailRouteAsync("company-that-folded.com"); // false
Zwei nachahmenswerte Idiome. Erstellen Sie den LookupClient einmal und
verwenden Sie ihn wieder — er ist thread-sicher und cacht Antworten, eine Instanz
pro Anfrage wirft diesen Cache also einfach weg. Und trennen Sie die Domain am
letzten @ mit einem Range-Ausdruck von der Adresse ab, damit Sie eine Adresse,
die (rechtmäßig) mehr als ein @ enthält, nie falsch parsen:
var domain = email[(email.LastIndexOf('@') + 1)..];
Manche Domains nehmen Mail über einen A-Eintrag ohne MX an (ein implizites MX).
Wenn Sie diesen Grenzfall berücksichtigen möchten, greifen Sie auf QueryType.A
zurück, wenn die MX-Menge leer ist — aber für die überwältigende Mehrheit echter
Adressen ist eine MX-Prüfung der richtige Filter. Eine Warnung für einen
Anmeldepfad: Eine DNS-Abfrage blockiert an einem Resolver, ein vorübergehender
DNS-Aussetzer sollte also keinen echten Kunden hart ablehnen. Behandeln Sie einen
fehlgeschlagenen Lookup als „später prüfen“, nicht als „ungültig“.
Schicht 3: Existiert das Postfach tatsächlich?#
Schicht eins und zwei können eine Adresse nur ausschließen. Eine Domain kann
perfekte MX-Einträge veröffentlichen und trotzdem kein Postfach an der Adresse
haben, die Sie in der Hand halten — noreply-9f2x@gmail.com ist gültige Syntax
auf einer Domain mit einer aktiven Mail-Route, und es ist trotzdem ein Postfach,
das nie angelegt wurde. Zu bestätigen, dass ein bestimmtes Postfach existiert,
bedeutet die SMTP-Zustellkonversation: sich mit dem Mailhost verbinden, RCPT TO
absetzen, die Antwort lesen und die Verbindung trennen, bevor irgendetwas
gesendet wird. Wie E-Mail-Verifizierung funktioniert
geht diese vollständige Pipeline durch, einschließlich Catch-all-Domains, die
jede Adresse annehmen und eine naive Prüfung aushebeln.
Im Prinzip können Sie das in C# mit einem TcpClient und rohen SMTP-Befehlen
skripten. In der Praxis sollten Sie es nicht von Ihrem Anwendungsserver aus
ausführen. Die meisten Cloud-Anbieter blockieren den ausgehenden Port 25, eine
Prüfung, die auf Ihrem Laptop funktioniert, scheitert also im Produktivbetrieb
still und leise. Die Antwort hängt von der Reputation der IP ab, von der aus Sie
sich verbinden, nicht nur von der Adresse, die Sie prüfen. Und empfangende Server
setzen unbekannte Absender auf Greylisting und Rate-Limits, Prüfen in jeglichem
Volumen bringt Ihnen also Verzögerungen oder einen Eintrag auf Sperrlisten ein.
Das ist die Schicht, die man an dafür gebaute Infrastruktur delegieren sollte.
Eine Verifizierungs-API aus C# aufrufen#
Der Verifizierungs-Endpunkt von Qualisend führt die gesamte Pipeline aus —
Syntax, DNS und die SMTP-Postfachprüfung — von reputationsverwalteter
Infrastruktur aus und liefert ein Verdikt in Echtzeit zurück. HttpClient plus
System.Text.Json ist alles, was Sie brauchen; modellieren Sie den
Antwort-Umschlag als Records und lassen Sie den JSON-Source-Generator oder den
Reflection-Deserializer sie füllen:
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json.Serialization;
public record VerifyResponse(
[property: JsonPropertyName("result")] VerifyResult Result);
public record VerifyResult(
[property: JsonPropertyName("status")] string Status,
[property: JsonPropertyName("score")] int Score,
[property: JsonPropertyName("reason")] string? Reason,
[property: JsonPropertyName("sub_flags")] IReadOnlyDictionary<string, bool> SubFlags);
// One HttpClient for the whole app — don't new one up per request.
private static readonly HttpClient Http = new()
{
BaseAddress = new Uri("https://api.qualisend.com/v1/"),
};
public static async Task<VerifyResult> VerifyAsync(string email)
{
using var request = new HttpRequestMessage(HttpMethod.Post, "verify")
{
Content = JsonContent.Create(new { email }),
};
request.Headers.Authorization = new AuthenticationHeaderValue(
"Bearer", Environment.GetEnvironmentVariable("QUALISEND_API_KEY"));
using var response = await Http.SendAsync(request);
response.EnsureSuccessStatusCode();
var body = await response.Content.ReadFromJsonAsync<VerifyResponse>();
return body!.Result; // status: deliverable | risky | undeliverable | unknown
}
POST https://api.qualisend.com/v1/verify ist eine Platzhalterform — prüfen Sie
die API-Referenz unter /developers für den genauen Endpunkt, das
Format des Scoped Keys und jedes Feld. Lesen Sie den Key aus einer
Umgebungsvariable, statt YOUR_API_KEY fest zu codieren, und verwenden Sie einen
einzigen HttpClient wieder, um Socket-Erschöpfung zu vermeiden. Die Antwort
kommt in einem result-Umschlag zurück:
{
"result": {
"status": "deliverable",
"score": 95,
"reason": null,
"sub_flags": { "role": false, "disposable": false, "free": false, "catch_all": false }
}
}
status ist Ihr Kernverdikt — deliverable, risky, undeliverable oder
unknown. score bewertet die Konfidenz, reason erklärt ein negatives
Ergebnis, und sub_flags schlüsselt Signale wie Role, Disposable, Free und
Catch-all auf, sodass Sie Ihre eigene Policy anwenden können (undeliverable
ablehnen, risky zur Prüfung zurückhalten, disposable bei der Anmeldung
markieren).
Die Schichten zusammensetzen, um eine E-Mail-Adresse in C# zu validieren#
Das Günstigste zuerst, aufhören, sobald Sie eine Antwort haben:
public static async Task<VerifyResult> ValidateEmailAsync(string email)
{
if (!IsValidSyntax(email))
return new VerifyResult("undeliverable", 0, "invalid_email",
new Dictionary<string, bool>());
var domain = email[(email.LastIndexOf('@') + 1)..];
if (!await HasMailRouteAsync(domain))
return new VerifyResult("undeliverable", 0, "invalid_domain",
new Dictionary<string, bool>());
return await VerifyAsync(email); // deliverable | risky | undeliverable | unknown
}
Die beiden lokalen Schichten kosten nichts und fangen den meisten Müll sofort ab; die API-Schicht läuft nur auf Adressen, die den Netzwerk-Roundtrip wert sind. Diese Reihenfolge ist der ganze Trick — dieselbe Form finden Sie in den Versionen dieses Leitfadens für Node.js, Python und PHP, denn es ist die Schichtung, nicht die Sprache, die Validierung funktionieren lässt.
Häufig gestellte Fragen#
Reicht MailAddress oder EmailAddressAttribute aus, um eine E-Mail in C# zu validieren?#
Für die Syntax ja — beide sind solide Prüfungen der ersten Schicht und die
bessere Wahl gegenüber einer selbst gebauten Regex, wobei
MailAddress.TryCreate Ihnen einen Boolean ohne Exception liefert. Sie
validieren aber die Form, nicht die Zustellbarkeit: Keines von beiden löst DNS
auf oder kontaktiert einen Mailserver, ein Bestehen bedeutet also „sieht aus wie
eine E-Mail“, nicht „wird zugestellt“. Kombinieren Sie die Syntaxprüfung mit
einem MX-Lookup und einer SMTP-Postfachprüfung, bevor Sie der Adresse vertrauen.
Bietet .NET eine eingebaute Möglichkeit, MX-Einträge abzufragen?#
Nein. System.Net.Dns löst A- und AAAA-Host-Einträge auf, unterstützt aber kein
MX, weshalb der Standardansatz das NuGet-Paket DnsClient.NET ist. Fragen Sie
QueryType.MX ab, verwenden Sie einen einzigen LookupClient wieder und
behandeln Sie eine leere Antwortmenge — oder eine DnsResponseException — als
fehlende Mail-Route, statt sie eine Exception werfen zu lassen.
Kann ich in C# ohne API prüfen, ob ein Postfach existiert?#
Nur teilweise. DnsClient.NET bestätigt, dass die Domain Mail annimmt, was tote
Domains kostengünstig ausschließt. Das tatsächliche Postfach zu bestätigen
bedeutet eine SMTP-Konversation, die Sie mit einem TcpClient versuchen können,
aber nicht von Ihrem App-Server aus ausführen sollten — der ausgehende Port 25
ist weitverbreitet blockiert und das Ergebnis hängt von der Reputation Ihrer
sendenden IP ab. Das ist die Schicht, für die es einen Verifizierungsdienst gibt,
und es lohnt sich, Anbieter zu vergleichen,
bevor Sie es selbst bauen.
Sollte ich E-Mails bei der Anmeldung oder beim Bereinigen einer Liste validieren?#
Beides, in unterschiedlicher Tiefe. Führen Sie Syntax und MX-Lookup bei der Anmeldung synchron aus — sie sind schnell genug, um die Anfrage zu blockieren und sofortiges Feedback zu geben — und handeln Sie auch dort nach dem unmittelbaren API-Verdikt; der Leitfaden zur serverlosen Anmeldung zeigt das Muster durchgängig. Reservieren Sie die tiefere, gebündelte Verifizierung für das Bereinigen einer bestehenden Liste, wo Latenz keine Rolle spielt, Gründlichkeit aber schon.
Bereit, die SMTP-Schicht hinzuzufügen? Die API-Referenz enthält
die genaue /verify-Form, Scoped Keys und Copy-and-paste-Beispiele, oder werfen
Sie eine Adresse in den kostenlosen E-Mail-Checker, um zu
beobachten, wie eine von MailAddress genehmigte Zeichenkette als
undeliverable zurückkommt.