public static bool IsValidFormat(string email)
{
if (string.IsNullOrWhiteSpace(email)) return false;
// RFC 5322 簡化版,涵蓋 99% 常見格式
const string pattern = @"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$";
return Regex.IsMatch(email, pattern, RegexOptions.IgnoreCase);
}
原理:連接郵件伺服器 25 埠 → HELO → MAIL FROM → RCPT TO(測試目標信箱)→ 觀察回應碼。注意:許多郵件伺服器為防垃圾郵件已關閉 VRFY/RCPT 探測,或回應統一 250 誘騙驗證者,故此法不可保證 100% 準確,僅供參考。
public static bool VerifySmtpExists(string email)
{
try
{
string domain = email.Split('@')[1];
IPHostEntry mx = Dns.GetHostEntry(domain); // 簡化:實際應查 MX 記錄
using (var client = new SmtpClient(mx.AddressList[0].ToString(), 25))
{
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.UseDefaultCredentials = false;
// 這裡需手工發送 SMTP 指令,.NET SmtpClient 不暴露 RCPT TO
// 建議使用 MailKit 或自行 Socket 實作完整 SMTP 交互
}
return true; // 簡化示意
}
catch { return false; }
}
// NuGet: MailKit
using MailKit.Net.Smtp;
using MailKit;
public async Task VerifyWithMailKitAsync(string email)
{
string domain = email.Split('@')[1];
var mxRecords = await DnsClient.LookupClient.QueryAsync(domain, QueryType.MX);
var mx = mxRecords.Answers.MxRecords().OrderBy(r => r.Preference).FirstOrDefault();
if (mx == null) return false;
using (var client = new SmtpClient())
{
await client.ConnectAsync(mx.HostName, 25, SecureSocketOptions.StartTlsWhenAvailable);
await client.HelloAsync("mydomain.com");
// MAIL FROM
var sender = new MailboxAddress("Verify", "verify@mydomain.com");
// RCPT TO
var recipient = new MailboxAddress("", email);
var status = await client.SendAsync(sender, new[] { recipient }, new MimeMessage());
return status != null && status.FailedRecipients.Count == 0;
}
}
| 場景 | 建議做法 |
|---|---|
| 使用者註冊表單 | Regex 即時檢查 → 發送驗證信(唯一可靠方式) |
| 大量清洗名單 | Regex 過濾 → SMTP 探測(接受誤判) → 最後發驗證信 |
| 高安全性系統 | 僅信任驗證信點擊回呼,不做 SMTP 探測 |
Regex 是必要條件,驗證信是充分條件。 SMTP 探測介於兩者之間,受限於反垃圾郵件機制,建議僅作輔助參考,切勿作為唯一判斷依據。