Ответ 1
С Rails 3.0 вы можете использовать проверку электронной почты без регулярного выражения с помощью Mail gem.
Что вы используете для проверки адресов электронной почты пользователей и почему?
Я использовал validates_email_veracity_of
, который фактически запрашивает серверы MX. Но это поломка по разным причинам, в основном связанная с сетевым трафиком и надежностью.
Я огляделся, и я не мог найти ничего очевидного, что многие люди используют для проверки здравого смысла на адрес электронной почты. Есть ли для этого поддерживаемый, достаточно точный плагин или драгоценный камень?
P.S.: Пожалуйста, не говорите мне, чтобы отправить электронное письмо со ссылкой, чтобы узнать, работает ли электронная почта. Я разрабатываю функцию "Отправить другу", поэтому это не практично.
С Rails 3.0 вы можете использовать проверку электронной почты без регулярного выражения с помощью Mail gem.
Не делай этого сложнее, чем нужно. Ваша функция некритична; валидация просто основного шага разумности, чтобы поймать опечатки. Я бы сделал это с простым регулярным выражением и не тратил процессорные циклы на что-то слишком сложное:
/\A[A-Za-z0-9._%+-][email protected][A-Za-z0-9.-]+\.[A-Za-z]+\z/
Это было адаптировано из http://www.regular-expressions.info/email.html - которое вы должны прочитать, если действительно хотите знать все компромиссы. Если вы хотите получить более правильное и намного более сложное полнофункциональное регулярное выражение, совместимое с RFC822, то и на этой странице. Но дело в том, что вам не нужно полностью понимать это.
Если адрес проходит проверку, вы отправите электронное письмо. Если сообщение не удалось, вы получите сообщение об ошибке. В этот момент вы можете сказать пользователю "Извините, ваш друг не получил этого, вы бы хотели попробовать еще раз?" или отметьте его для ручного просмотра или просто проигнорируйте его или что-то еще.
Это те же самые параметры, с которыми вам пришлось бы иметь дело, если бы адрес прошел проверку. Потому что даже если ваша проверка является совершенной, и вы получаете абсолютное доказательство того, что адрес существует, отправка может все же сбой.
Стоимость ложного положительного результата при проверке низкая. Преимущество лучшей проверки также низкое. Подчинитесь щедро и будьте обеспокоены ошибками, когда они произойдут.
Я создал жемчужину для проверки электронной почты в Rails 3. Я удивлен, что Rails не включает в себя что-то подобное по умолчанию.
В настоящее время этот проект имеет наибольшее количество наблюдателей в github (для проверки подлинности электронной почты в рельсах):
Из Rails 4 docs:
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{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, это зависит от того, насколько вы хотите быть основательным. В большинстве случаев его регулярное выражение достаточно. Я просто использую ruby TMail library для проверки юридического адреса электронной почты any, возможно, за счет некоторых циклов процессора.
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
Ваш валидатор (идет в 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
Это позволит использовать всевозможные почтовые сообщения, включая тегированные сообщения, такие как "[email protected]" и т.д.
Чтобы проверить это с помощью rspec
в spec/validators/email_validator_spec.rb
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) { '[email protected]' }
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) { '[email protected]' }
it "is accepted as valid" do
model.errors[].should_not_receive('<<')
validator.validate_each(model, "email", valid_tagged_email)
end
end
end
Вот как я это сделал. YMMV
† Регулярные выражения напоминают насилие; если они не работают, вы не используете их достаточно.
В Rails 3 можно написать валидатор повторно используемого, так как этот отличный пост объясняет:
class EmailValidator < ActiveRecord::Validator
def validate()
record.errors[:email] << "is not valid" unless
record.email =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
end
end
и используйте его с validates_with
:
class User < ActiveRecord::Base
validates_with EmailValidator
end
Как Аллилуйя предполагает, что использование Mail 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 ([email protected])
# 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 (например, добавление 2012 года .mobi
и .tel
)
Преимущество прямого подключения парсера заключается в том, что правила в грамматике почты довольно широки для частей, которые использует почтовый жемчуг, это разработанный, чтобы позволить ему анализировать такой адрес, как user<[email protected]>
, который является общим для SMTP. Потребив его из Mail::Address
, вы вынуждены выполнять кучу дополнительных проверок.
Еще одна заметка о жемчужине Mail, хотя класс называется RFC2822, в грамматике есть некоторые элементы RFC5322, например этот тест.
Отмечая другие ответы, вопрос все еще остается - зачем беспокоиться об этом?
Фактический объем крайних случаев, которые многие регулярные выражения могут отрицать или пропустить, кажется проблематичным.
Я думаю, что вопрос: "Что я пытаюсь добиться?", даже если вы "проверяете" адрес электронной почты, вы на самом деле не проверяете, что это рабочий адрес электронной почты.
Если вы переходите на регулярное выражение, просто проверьте наличие @на стороне клиента.
Что касается неправильного сценария электронной почты, у вас есть сообщение "не удалось отправить" ветке в ваш код.
В основном есть 3 наиболее распространенных варианта:
Если вы не хотите использовать как validates_email_veracity_of, так и маркеры, я бы пошел с проверкой регулярного регулярного обновления школы.
Почтовый жемчуг имеет встроенный парсер адресов.
begin
Mail::Address.new(email)
#valid
rescue Mail::Field::ParseError => e
#invalid
end
Это решение основано на ответах @SFEley и @Alessandro DS с рефактором и разъяснением использования.
Вы можете использовать этот класс проверки в своей модели следующим образом:
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._%+-][email protected][A-Za-z0-9.-]+\.[A-Za-z]+\z/.match(value).nil?
false
else
true
end
end
end
Для Проверка почтовых списков. (Я использую Rails 4.1.6)
Я получил свое regexp из здесь. Он кажется очень полным, и он был протестирован против большого количества комбинаций. Вы можете увидеть результаты на этой странице.
Я немного изменил его на регулярное выражение Ruby и поместил его в мой 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,}(?:(?:[a-z][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
Он будет проверять списки рассылки, подобные этой, с разными разделителями и синтаксисом:
mail_list = 'John Doe <[email protected]>, [email protected]; David G. <[email protected]>'
Прежде чем использовать это регулярное выражение, я использовал Devise.email_regexp
, но это очень простое регулярное выражение и не получило всех необходимых мне случаев. Некоторые электронные письма столкнулись.
Я пробовал другие регулярные выражения из Интернета, но до сих пор этот результат получил лучшие результаты. Надеюсь, это поможет в вашем случае.