Rails电子邮件validation的最新技术是什么?

你用什么来validation用户的电子邮件地址,为什么?

我一直在使用实际查询MX服务器的validates_email_veracity_of 。 但是由于种种原因,这种情况充满了失败,主要与networkingstream量和可靠性有关。

我环顾四周,找不到有很多人用来对电子邮件地址进行健全性检查的事情。 有没有一个维护,相当准确的插件或创业板?

PS:请不要告诉我发送带链接的电子邮件,看看电子邮件是否有效。 我正在开发一个“发送给朋友”的function,所以这是不实际的。

使用Rails 3.0,您可以使用邮件validation而不使用正则expression式使用邮件gem 。

这是我的实现 ( 打包成一个gem )。

不要让它比需要的更难。 你的function是不重要的; validation只是一个基本的理智步骤,以捕获错别字。 我会做一个简单的正则expression式,而不是浪费在任何太复杂的CPU周期:

 /\A[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+\z/ 

这是从http://www.regular-expressions.info/email.html改编; – 如果你真的想知道所有的权衡,你应该阅读。 如果你想要一个更正确,更复杂的RFC822兼容的正则expression式,那也是在那个页面上。 但事情是这样的: 你不必完全正确。

如果地址通过validation,您将发送电子邮件。 如果电子邮件失败,您将收到错误消息。 在这一点上,你可以告诉用户“对不起,你的朋友没有收到,你想再试一次吗?” 或将其标记为手动审查,或者只是忽略它,或其他任何东西。

如果地址通过了validation,则这些选项与您必须处理的选项相同。 因为即使您的validation是完美的,并且您获得了地址存在的绝对证据,发送仍可能失败。

validation误报的成本很低。 更好的validation的好处也很低。 慷慨地确认,并在发生错误时担心。

我在Rails 3中创build了一个用于电子邮件validation的gem。我有点惊讶Rails在默认情况下不包含这样的内容。

http://github.com/balexand/email_validator

目前这个项目似乎在github上的观察者最多(在rails中进行电子邮件validation):

https://github.com/alexdunae/validates_email_format_of

从Rails 4文档 :

 class EmailValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[az]{2,})\z/i record.errors[attribute] << (options[:message] || "is not an email") end end end class Person < ActiveRecord::Base validates :email, presence: true, email: true end 

更新 :TMail已经被Mail Gem所取代

就像SFEley说的那样,取决于你想成为多么彻底。 在大多数情况下,他的正则expression式就足够了。 我只是使用ruby的TMail库来validation任何合法的电子邮件地址,也许在一些CPU周期的代价。

 begin TMail::Address.parse(email_address) return true rescue return false end 

在Rails 4中,只需在你的模型中添加validates :email, email:true (假设你的字段被称为email ),然后编写一个简单的(或复杂的) EmailValidator来满足你的需求。

例如: – 您的模型:

 class TestUser include Mongoid::Document field :email, type: String validates :email, email: true end 

validation器(进入app/validators/email_validator.rb

 class EmailValidator < ActiveModel::EachValidator EMAIL_ADDRESS_QTEXT = Regexp.new '[^\\x0d\\x22\\x5c\\x80-\\xff]', nil, 'n' EMAIL_ADDRESS_DTEXT = Regexp.new '[^\\x0d\\x5b-\\x5d\\x80-\\xff]', nil, 'n' EMAIL_ADDRESS_ATOM = Regexp.new '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+', nil, 'n' EMAIL_ADDRESS_QUOTED_PAIR = Regexp.new '\\x5c[\\x00-\\x7f]', nil, 'n' EMAIL_ADDRESS_DOMAIN_LITERAL = Regexp.new "\\x5b(?:#{EMAIL_ADDRESS_DTEXT}|#{EMAIL_ADDRESS_QUOTED_PAIR})*\\x5d", nil, 'n' EMAIL_ADDRESS_QUOTED_STRING = Regexp.new "\\x22(?:#{EMAIL_ADDRESS_QTEXT}|#{EMAIL_ADDRESS_QUOTED_PAIR})*\\x22", nil, 'n' EMAIL_ADDRESS_DOMAIN_REF = EMAIL_ADDRESS_ATOM EMAIL_ADDRESS_SUB_DOMAIN = "(?:#{EMAIL_ADDRESS_DOMAIN_REF}|#{EMAIL_ADDRESS_DOMAIN_LITERAL})" EMAIL_ADDRESS_WORD = "(?:#{EMAIL_ADDRESS_ATOM}|#{EMAIL_ADDRESS_QUOTED_STRING})" EMAIL_ADDRESS_DOMAIN = "#{EMAIL_ADDRESS_SUB_DOMAIN}(?:\\x2e#{EMAIL_ADDRESS_SUB_DOMAIN})*" EMAIL_ADDRESS_LOCAL_PART = "#{EMAIL_ADDRESS_WORD}(?:\\x2e#{EMAIL_ADDRESS_WORD})*" EMAIL_ADDRESS_SPEC = "#{EMAIL_ADDRESS_LOCAL_PART}\\x40#{EMAIL_ADDRESS_DOMAIN}" EMAIL_ADDRESS_PATTERN = Regexp.new "#{EMAIL_ADDRESS_SPEC}", nil, 'n' EMAIL_ADDRESS_EXACT_PATTERN = Regexp.new "\\A#{EMAIL_ADDRESS_SPEC}\\z", nil, 'n' def validate_each(record, attribute, value) unless value =~ EMAIL_ADDRESS_EXACT_PATTERN record.errors[attribute] << (options[:message] || 'is not a valid email') end end end 

这将允许各种有效的电子邮件,包括像“test+no_really@test.tes”等标记的电子邮件。

spec/validators/email_validator_spec.rb中用rspec来testing

 require 'spec_helper' describe "EmailValidator" do let(:validator) { EmailValidator.new({attributes: [:email]}) } let(:model) { double('model') } before :each do model.stub("errors").and_return([]) model.errors.stub('[]').and_return({}) model.errors[].stub('<<') end context "given an invalid email address" do let(:invalid_email) { 'test test tes' } it "is rejected as invalid" do model.errors[].should_receive('<<') validator.validate_each(model, "email", invalid_email) end end context "given a simple valid address" do let(:valid_simple_email) { 'test@test.tes' } it "is accepted as valid" do model.errors[].should_not_receive('<<') validator.validate_each(model, "email", valid_simple_email) end end context "given a valid tagged address" do let(:valid_tagged_email) { 'test+thingo@test.tes' } it "is accepted as valid" do model.errors[].should_not_receive('<<') validator.validate_each(model, "email", valid_tagged_email) end end end 

无论如何,我都是这样做的。 因人而异

†正则expression式就像暴力; 如果他们不工作,你没有使用足够的。

哈利路亚build议我认为使用邮件gem是一个好方法。 但是,我不喜欢那里的一些篮球。

我用:

 def self.is_valid?(email) parser = Mail::RFC2822Parser.new parser.root = :addr_spec result = parser.parse(email) # Don't allow for a TLD by itself list (sam@localhost) # The Grammar is: (local_part "@" domain) / local_part ... discard latter result && result.respond_to?(:domain) && result.domain.dot_atom_text.elements.size > 1 end 

您可能会更严格地要求顶级域名(TLD)位于此列表中 ,但是当新的顶级域名(TLD)popup时,您将被迫更新该列表(如2012年新增的.mobi.tel

直接连接parsing器的好处在于,邮件语法中的规则对于邮件gem所使用的部分来说相当宽泛,它被devise为允许它parsing像SMTP常见的user<user@example.com>这样的地址。 通过从Mail::Address消耗它,你不得不做一堆额外的检查。

关于Mail gem的另一个注意事项是,尽pipe这个类叫做RFC2822,但是这个语法有一些RFC5322的元素,比如这个testing 。

在Rails 3中,可以编写一个可重用的validation器,正如这篇伟大的文章所解释的:

http://archives.ryandaigle.com/articles/2009/8/11/what-s-new-in-edge-rails-independent-model-validators

 class EmailValidator < ActiveRecord::Validator def validate() record.errors[:email] << "is not valid" unless record.email =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[az]{2,})$/i end end 

并将其与validates_with一起使用:

 class User < ActiveRecord::Base validates_with EmailValidator end 

注意到其他的答案,问题仍然存在 – 为什么还要聪明呢?

许多正则expression式可能否认或错过的边缘情况的实际数量似乎有问题。

我认为这个问题是“我想要达到什么目的?”,即使你“validation”了电子邮件地址,你也没有真正确认这是一个有效的电子邮件地址。

如果你使用正则expression式,只需检查客户端是否存在@。

至于不正确的电子邮件scheme,有一个“消息未能发送”分支到您的代码。

基本上有三种最常见的select:

  1. 正则expression式(有没有工作的所有电子邮件地址正则expression式,所以推出自己的)
  2. MX查询(这是你使用的)
  3. 生成激活令牌并将其邮寄(restful_authentication方式)

如果你不想同时使用validates_email_veracity_of和token生成,我会用老式的正则expression式检查。

邮件gem有一个内置的地址parsing器。

 begin Mail::Address.new(email) #valid rescue Mail::Field::ParseError => e #invalid end 

此解决scheme基于@SFEley和@Alessandro DS的解答,并进行重构和使用说明。

你可以在你的模型中使用这个validation器类,如下所示:

 class MyModel < ActiveRecord::Base # ... validates :colum, :email => { :allow_nil => true, :message => 'O hai Mark!' } # ... end 

鉴于你的app/validators文件夹(Rails 3)中有以下内容:

 class EmailValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) return options[:allow_nil] == true if value.nil? unless matches?(value) record.errors[attribute] << (options[:message] || 'must be a valid email address') end end def matches?(value) return false unless value if /\A[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+\z/.match(value).nil? false else true end end end 

邮件列表validation 。 (我使用Rails 4.1.6)

我从这里得到了我的正则expression式。 这似乎是一个非常完整的,它已经过大量的组合testing。 您可以在该页面上看到结果。

我稍微改变了一个Ruby的正则expression式,并把它放在我的lib/validators/email_list_validator.rb

代码如下:

 require 'mail' class EmailListValidator < ActiveModel::EachValidator # Regexp source: https://fightingforalostcause.net/content/misc/2006/compare-email-regex.php EMAIL_VALIDATION_REGEXP = Regexp.new('\A(?!(?:(?:\x22?\x5C[\x00-\x7E]\x22?)|(?:\x22?[^\x5C\x22]\x22?)){255,})(?!(?:(?:\x22?\x5C[\x00-\x7E]\x22?)|(?:\x22?[^\x5C\x22]\x22?)){65,}@)(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C[\x00-\x7F]))*\x22))(?:\.(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C[\x00-\x7F]))*\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-[a-z0-9]+)*\.){1,126}){1,}(?:(?:[az][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-[a-z0-9]+)*)|(?:\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))\]))\z', true) def validate_each(record, attribute, value) begin invalid_emails = Mail::AddressList.new(value).addresses.map do |mail_address| # check if domain is present and if it passes validation through the regex (mail_address.domain.present? && mail_address.address =~ EMAIL_VALIDATION_REGEXP) ? nil : mail_address.address end invalid_emails.uniq! invalid_emails.compact! record.errors.add(attribute, :invalid_emails, :emails => invalid_emails.to_sentence) if invalid_emails.present? rescue Mail::Field::ParseError => e # Parse error on email field. # exception attributes are: # e.element : Kind of element that was wrong (in case of invalid addres it is Mail::AddressListParser) # e.value: mail adresses passed to parser (string) # e.reason: Description of the problem. A message that is not very user friendly if e.reason.include?('Expected one of') record.errors.add(attribute, :invalid_email_list_characters) else record.errors.add(attribute, :invalid_emails_generic) end end end end 

我在模型中使用它:

 validates :emails, :presence => true, :email_list => true 

它将validation这样的邮件列表,不同的分隔符和合成器:

 mail_list = 'John Doe <john@doe.com>, chuck@schuld.dea.th; David G. <david@pink.floyd.division.bell>' 

在使用这个正则expression式之前,我使用了Devise.email_regexp ,但这是一个非常简单的正则expression式,并没有得到我需要的所有情况。 一些电子邮件碰撞。

我从网上尝试了其他的正则expression式,但是这个直到现在才得到了最好的结果。 希望它有助于你的情况。