目录
验证电子邮件地址很困难,因为构成有效电子邮件地址的规范允许使用大量电子邮件。试图将这些规范转换为适当的Regex会导致庞大而复杂的表达式,但仍可能无法涵盖所有情况。本文介绍如何合理地完成电子邮件地址验证,并提供生产质量代码WPF文本框,包括让用户仅输入有效的击键(keystrokes),控制是否必须输入电子邮件地址(必填字段),,以及在用户试图关闭窗口而不保存所做的更改时发出警报。
介绍
编写用于验证电子邮件地址的WPF控件是一个挑战,因为几乎允许所有Unicode字符和许多格式。规范允许使用多种格式,但实际上只使用了很少的格式,因为异国情调的格式可能会被某些电子邮件软件(Outlook等客户端,Exchange等服务器)拒绝。控件必须足够灵活才能满足您的要求,即,如果您需要使用精确格式的严格控件,或者只想在用户键入奇怪的电子邮件地址时提醒用户。当然,最好的办法是防止用户进行无效的输入并控制用户可以进行的击键(keystrokes)。由于此控件是WpfWindowLib的一部分,因此如果需要或缺少电子邮件地址,它不允许用户保存数据。它还会通知用户是否试图关闭窗口而不保存数据。
电邮地址格式
相关规范可以在RFC 5322:Internet消息格式:地址规范中找到。
它通过两个步骤定义了一个有效的电子邮件地址:
1. Address
这个更高的级别指定了一个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是电子邮件服务器的互联网DNS地址,同时local-part是该电子邮件服务器中的“信箱”的名称。domain-part的定义非常明确,每个人都需要了解电子邮件地址的路由,而本地部分的确切含义是由接收电子邮件服务器软件定义的,发件人不一定需要了解本地部分结构。该规范希望给电子邮件服务器尽可能的自由,这使得很难验证电子邮件地址是否正确。
有效电子邮件地址的唯一公认的要求是:
必须有两个部分,正好用'@'分隔。
但是,即使这个简单的规范也不总是正确的,因为以下也是有效的电子邮件地址:
"John@Doe"@example.com
第一个'@'在带引号的字符串中。所有可见的ASCII字符(即,从0x21到0x7E)被允许在local-part中使用,当它们被引用时。它们可以在两个引号'“'之间,也可以在单个特殊字符后加反斜杠'\':
John\@Doe@example.com (这是与上面带有引号的字符串相同的地址)。
有效的电子邮件地址
(灵感来自https://en.wikipedia.org/wiki/Email_address#Examples和RFC 3696名称检查和转换的应用技术:电子邮件地址的限制)
simple@example.com
域(Domain)应该包含“.”,因为根域不能是电子邮件服务器的地址。当然,对于大多数规则,也有一个例外:example@localhost。该域地址不是用于Internet,而是用于公司的内部网络。
x@example.com
一个字母的local-part是可以的。
John.Doe@example.com
句点“.”字符遵循一些特殊的规则:它不能是local-part或domain-part的第一或的最后一个字母并不能有连续的2个点像“..”。
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
引号之间的空格是邮箱的名称。在2个双引号之间,几乎没有任何变化。这使验证变得困难。
"john..doe"@example.org
带双引号的引号,不允许带引号
Some\@saple@example.com
第一个@应被视为一个简单的文字,而不是作为控制字符从domain-part分隔local-part。
John\ Doe@example.com
空格只允许被引用(在RFC的说法中,引用也意味着一个前导反斜杠'\'。
John.\\Doe@example.com
第一个反斜杠“\”使第二个反斜杠“\”成为普通字符。
一些看起来很奇怪的有效地址
"very.(),:;<>[]\".VERY.\"very@\ \"very\".unusual"@strange.example.com
mailhost!username@example.org
用于uucp邮件的分类主机路由
user%example.com@example.org
%通过example.org路由到了user@example.com
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,因为必须从可能不支持UTF8的DNS服务器中查找实际IP地址。因此,发明了PunyCode,它以纯ASCII编码UTF8,然后也可以由无UTF8域服务器处理。但是您的电子邮件软件是否支持Punycode?
如前所述,目标是使用不会引起麻烦的电子邮件地址。使用基于UTF8的地址会带来麻烦。如果必须的话,请接受UTF8,但是如果不这样做,您的生活会轻松得多。
长度限制
除了语法限制外,电子邮件地址还有长度限制。该限制最大为64个字符(UTF8字节),local-part最大为255个字符(UTF8字节),domain-part总长度为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。
编写电子邮件验证以正确识别所有合法和非法电子邮件地址非常困难,有人说几乎是不可能的。即使有可能,也并不意味着该电子邮件地址确实有效。验证的唯一方法是发送电子邮件并等待回复。因此,最好是使用一个相对简单的验证来警告用户,如果某些东西看起来很奇怪,从而发现错别字,但将最终决定权留给用户。
使用代码
<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>
显示的上部窗口是带有名称TextBox的数据输入窗口,用户在保存之前必须在其中输入一些数据,以及EmailTextBox。TextBox需要一个名字,但用户没有输入任何数据,这就是为什么背景是卡其色。这也是禁用“保存”按钮的原因。用户已输入电子邮件地址。然后,用户尝试关闭窗口而不保存数据。打开第二个警告窗口,“电子邮件TextBox”的背景变为浅绿色,以向用户显示哪些数据已更改。有关如何工作的详细说明,请参阅我的文章“用于数据输入的基本WPF窗口功能”。
在此屏幕快照中,用户输入了名称。因此保存按钮被激活。用户单击“保存”按钮,但收到警告,因为该电子邮件地址看起来很奇怪(不是在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:设置为true用户是否应该能够使用大于0x7F的Unicode字符。
- 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单元测试