电子邮件地址验证:详细解释,用于生产级 WPF TextBox 的代码






3.95/5 (8投票s)
一个 WPF TextBox,可以根据您的需求验证电子邮件地址,并详细描述了有效电子邮件地址的多种可能外观。
目录
引言
编写一个验证电子邮件地址的 WPF 控件是一项挑战,因为几乎所有 Unicode 字符和许多格式都是允许的。规范允许多种多样,但实际上只有少数格式被使用,因为一些非常规的格式可能会被某些电子邮件软件(如 Outlook 客户端、Exchange 服务器)拒绝。该控件必须足够灵活以满足您的要求,即您是否需要严格控制精确的格式,或者您只是想在用户输入了一个奇怪的电子邮件地址时提醒他们。当然,最好是阻止用户进行无效输入,并控制他们可以进行的按键操作。由于此控件是 WpfWindowLib
的一部分,如果电子邮件地址是必需的但缺失,它将不允许用户保存数据。它还会通知用户,如果他们尝试在未保存数据的情况下关闭窗口。
电子邮件地址格式
相关规范可以在 RFC 5322:Internet Message Format: Address Specification 中找到。
它分 2 步定义了一个有效的电子邮件地址
1. 地址
这个更高级别的规范,除其他外,指定了一个 Address
可以由一个 display-name
和一个 Addr-Spec
组成,后者是我们通常在谈论电子邮件地址时所指的部分。Address
可能看起来像这样
John Doe<John.Doe@example.com>
- "John Doe" 是显示名称。它不用于电子邮件路由,但允许以更用户友好的形式显示电子邮件地址。
- "<John.Doe@example.com>":在规范中称为
angle-addr
,包含尖括号中的addr-spec
(用于电子邮件路由的电子邮件地址)。
显示名称是可选的。如果没有显示名称,则不需要尖括号。
2. addr-spec
这部分描述了通常所说的电子邮件地址
John.Doe@example.com
基本上,地址有两个部分
local-part
@ domain-part
domain-part
是电子邮件服务器的 Internet DNS 地址,而 local-part
是该电子邮件服务器上的“邮箱”名称。domain-part 定义得相当好,并且每个人都需要理解它来进行电子邮件地址的路由,而 local-part 的确切含义由接收电子邮件服务器软件定义,发送者不一定需要理解 local-part 的结构。规范希望给电子邮件服务器尽可能多的自由,这使得验证电子邮件地址是否正确变得困难。
唯一普遍同意的有效电子邮件地址要求是
必须有两个部分,由一个且仅一个 '@
' 分隔。
但即使是这个简单的规范也不总是正确的,因为以下内容也是一个有效的电子邮件地址
"John@Doe"@example.com
第一个 '@
' 在引号字符串中。当它们被引用时,所有可见的 ASCII 字符(即从 0x21 到 0x7E)都允许在 local-part
中使用。它们可以位于两个引号 '"' 之间,或者单个特殊字符前面可以有一个反斜杠 '\'
John\@Doe@example.com
(这与上面用引号字符串表示的地址相同)。
有效的电子邮件地址
(灵感来自 https://en.wikipedia.org/wiki/Email_address#Examples 和 RFC 3696 Application Techniques for Checking and Transformation of Names: Restrictions on email addresses)
simple@example.com
域名应该包含一个 '.
',因为根域名不能是电子邮件服务器的地址。当然,像大多数规则一样,也有例外:example@localhost
。这个域名地址不是用于 Internet,而是用于公司的内部网络。
x@example.com
单字母 local-part
是可以的。
John.Doe@example.com
句点 '.
' 字符遵循一些特殊规则:它不能是 local-part
或 domain-part
的第一个或最后一个字母,并且不能有两个连续的句点,例如 '..'。
Gmail 将 'John.Doe
' 和 'JohnDoe
' 视为相同的地址。如上所述,如何解释其邮箱地址取决于接收电子邮件服务器。但这在使用电子邮件地址来识别个人时(例如登录页面)会带来问题。它会假设 John.Doe
@example.com 和 JohnDoe
@example.com 是 2 个不同的人吗?
-minus-sign-@example.com
_under_score_@example.com
连字符 '-
' 和下划线 '_
' 在任何地方都被接受
John.Doe+Filter@example.com
可能合法,但所有电子邮件软件都理解吗?一些电子邮件服务器会将“John.Doe
”视为实际的邮箱名称,并忽略 '+' 及其后面的所有内容直到 '@'。
实际上,所有这些字符在 local-part
中都是合法的! # $ % & ' * + - / = ? ^ _ ` . { | } ~ 但并非所有电子邮件软件都会接受它们。可怜的 O’Leary@example.com,一些电子邮件客户端会将其发送给 OLeary@example.com。
显示名称
'<
' 和 '>
' 字符不在上述列表中,因为它们具有特殊含义,它们将“显示名称”与实际的“电子邮件地址”分开
My Name <MyName@Example.com>
显示名称只能在尖括号内的电子邮件地址之前。它可以包含与电子邮件地址相同的字符以及空格。
注释
括号 '(' 也不在上述列表中 ')'。它们用来包围注释
Name(Comment1)<(Comment2)Name(Comment3)@(Comment4)Example.com(Comment5>(Comment2)
注释用圆括号表示。注释可以包含除 '(', ')' 和 '\' 之外的任何可打印 ASCII 字符。这严格遵守 RFC5322,但我猜很多电子邮件软件不会正确解析它。有些甚至像上面描述的那样将其用作显示名称,这是错误的。如果可能,请勿使用注释。
引用
" "@example.org
引号之间的空格是邮箱的名称。在两个双引号之间,几乎任何内容都可以。这使得验证变得困难。
"john..doe"@example.org
带引号的双句点,在没有引号的情况下是不允许的
Some\@saple@example.com
第一个 @
应被视为普通字符,而不是分隔 local-part
和 domain-part
的控制字符。
John\ Doe@example.com
空格仅在被引用时允许(根据 RFC 的说法,引用也意味着一个前导反斜杠 '\')。
John.\\Doe@example.com
第一个反斜杠 '\' 使第二个反斜杠 '\' 成为普通字符。
一些看起来奇怪的有效地址
"very.(),:;<>[]\".VERY.\"very@\ \"very\".unusual"@strange.example.com
mailhost!username@example.org
用于 uucp 邮件程序的 Bangified 主机路由
user%example.com@example.org
% 转义的邮件路由到 user@example.com,经由 example.org
Domain-Part 要求
domain-part
是一个 DNS 主机名,由字母、数字、连字符和句点组成。在极少数情况下,可以使用 IP 地址代替,用方括号括起来
John.Doe@[192.168.0.0]
user@[IPv6:2001:db8::1]
使用非 ASCII 字符 (UTF8)
rfc6530, rfc6531, rfc6532 规定了如何使用任何 UTF 字符作为电子邮件地址,前提是电子邮件软件支持它。由于电子邮件经常从服务器转发到服务器再到服务器...,因此很可能其中一个服务器不支持 UTF8,并且会向发件人返回一条错误消息,表明电子邮件无法送达。因此,为电子邮件地址使用 UTF8 更安全,尽管以下示例实际上是有效的地址
Pelé@example.com
δοκιμή@παράδειγμα.δοκιμή
我買@屋企.香港
संपर्क@डाटामेल.भारतारत
domain-part
尤其麻烦,因为必须从 DNS 服务器查找实际的 IP 地址,而 DNS 服务器可能不支持 UTF8。因此,发明了 PunyCode,它将 UTF8 编码为纯 ASCII,然后非 UTF8 域名服务器也可以处理。但您的电子邮件软件支持 Punycode 吗?
如前所述,目标是使用不会引起麻烦的电子邮件地址。使用基于 UTF8 的地址是自找麻烦。如果您必须使用,请接受 UTF8,但如果您不使用,您的生活会容易得多。
长度限制
除了语法限制外,电子邮件地址还有一个长度限制。local-part
的最大长度为 64 个字符(UTF8 字节),domain-part
的最大长度为 255 个字符(UTF8 字节),总长度为 320 个字符。
无效的电子邮件地址
如上所述,电子邮件地址存在许多有效的结构。但也存在一些非法的结构
Abc.example.com
没有 @ 字符
A@b@c@example.com
在引号外只允许一个 @
,除非它被引用
john..doe@example..com
双句点 '..
' 在任一部分都不允许
a"b(c)d,e:f;g<h>i[j\k]l@example.com
此 local-part 中的特殊字符在引号外都不允许
just"not"right@example.com
带引号的字符串必须用点分隔,或者它们是构成 local-part 的唯一元素
1234567890123456789012345678901234567890123456789012345678901234+x@example.com
Local part 长度超过 64 个字符
建议
- 如果用户输入了一个看起来很奇怪的电子邮件地址,请警告用户。很可能是输入错误,但也可能只是一个看起来奇怪但有效的电子邮件地址。让用户自己决定。
- 通过减少用户可以输入的字符集,帮助用户限制他们可能犯的错误数量。如果可能,避免使用 UTF8。
编写一个电子邮件验证程序,能够正确识别所有合法和所有非法电子邮件地址非常困难,有些人甚至说几乎不可能。即使可能,这仍然不意味着电子邮件地址实际上有效。唯一验证方法是发送电子邮件并等待回复。因此,最好只使用一个相对简单的验证来在用户输入看起来奇怪的内容时发出警报,从而发现输入错误,但将最终决定权留给用户。
Using the Code
<wwl:CheckedWindow x:Class="Samples.SampleWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wwl="clr-namespace:WpfWindowsLib;assembly=WpfWindowsLib"
SizeToContent="WidthAndHeight">
<StackPanel>
<Label Content="Name (required)"/>
<wwl:CheckedTextBox x:Name="NameTextBox" MinWidth="100" IsRequired="True"/>
<Label Content="Email (not required)"/>
<wwl:EmailTextBox x:Name="EmailTextBox" MinWidth="100"/>
<Button x:Name="SaveButton" Content="_Save"/>
</StackPanel>
</wwl:CheckedWindow>
上面显示的窗口是数据录入窗口,其中有一个 Name TextBox
,用户在保存之前必须在此处输入一些数据,以及 EmailTextBox
。TextBox
需要一个名称,但用户尚未输入任何数据,因此背景为卡其色。这也是“保存”按钮被禁用的原因。用户已输入电子邮件地址。然后,用户尝试在未保存数据的情况下关闭窗口。一个带有警告的第二个窗口打开,Email TextBox
的背景变为浅绿色,以向用户显示已更改的数据。有关此工作原理的详细说明,请参阅我的文章 Base WPF window functionality for data entry。
在此截图中,用户已输入姓名。因此,“保存”按钮已启用。用户单击“保存”按钮,但由于电子邮件地址看起来很奇怪(domain-part
中没有 '.
')而收到警告,然后可以决定是否应保存电子邮件地址或需要进行进一步编辑。
配置 EmailTextBox
在许多情况下,您无需配置任何内容。您可能想在 XAML 中设置 IsRequired
,或者,如果用户要编辑现有数据,请在后台代码中调用 Initialise()
,传入现有的电子邮件地址和 isRequired
作为参数。
实例属性
某些属性可以为每个 PhoneTextBox
单独设置
IsRequired
(DependencyProperty
):用户是否需要为此控件提供值?MaxLength
(DependencyProperty
):用户可以手动输入的文本框中的最大字符数。
静态属性
某些属性适用于所有 PhoneTextBox
,因此被声明为 static
AsciiSpecialChars
(string
):除字母和数字外,在电子邮件地址的local-part
中允许的其他字符。默认值:“.@-_+
”。要允许更多字符,请分配自己的字符串或调用EmailTextBox.SetExtendedAsciiSpecialChars()
或EmailTextBox.SetExtendedQuotedAsciiSpecialChars()
。IsBlankAllowed
:如果用户应该能够输入空格,则设置为true
。IsInternationalCharSetAllowed
:如果用户应该能够使用大于 0x7F 的 Unicode 字符,则设置为true
。IsValidEmailChar
(Func<char, bool>
):在用户刚刚输入的字符是否允许在电子邮件地址的local-part
中进行验证时调用。如果要使用不同的验证,请分配自己的函数。IsValidDnsChar
(Func<char, bool>
):在用户刚刚输入的字符是否允许在电子邮件地址的domain-part
中进行验证时调用。如果要使用不同的验证,请分配自己的函数。IsValidEmail
(Func<string, bool>
):在键盘焦点离开EmailTextBox
时,用于验证整个电子邮件地址。如果要使用不同的验证,请分配自己的函数。ShowLooksStrangeWarning
(Func<EmailTextBox, bool>
):当IsValidEmail
检测到问题时调用。如果要以不同方式显示问题,请分配自己的函数。
获取代码
最新版本可在 Github 上获取:https://github.com/PeterHuberSg/WpfWindowsLib。
下载或克隆所有内容到您的 PC,这将为您提供一个名为 WpfWindowsLib
的解决方案,其中包含以下项目
WpfWindowsLib
: (.Dll) 将被您的其他解决方案引用,包含EmailTextBox
Samples
: WPF Core 应用程序,展示所有WpfWindowsLib
控件WpfWindowsLibTest
:包含一些WpfWindowsLib
单元测试
推荐阅读
- 用于数据输入的基类 WPF 窗口功能
- 国际电话号码验证详解
- 使用绑定进行 WPF DataGrid 格式化的指南
- POP3 Email Client with full MIME Support (超过 100 万次浏览,评分:4.88/5)
历史
- 2020 年 3 月 19 日:初始版本