Ответ 1
Я использую постоянную, встроенную в URI в стандартной библиотеке ruby
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
Я выполняю проверку электронной почты в Rails с помощью:
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
Кроме того, я проверяю HTML5 во внешнем интерфейсе, но адреса электронной почты, такие как
[email protected]
[email protected]
все еще действительны. Что мне не хватает?
Я использую постоянную, встроенную в URI в стандартной библиотеке ruby
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
Не используйте регулярное выражение для проверки адреса электронной почты. Это ловушка. Есть гораздо больше допустимых форматов адресов электронной почты, чем вы думаете. Тем не мение! Gem mail
(он требуется ActionMailer, поэтому он у вас есть) проанализирует адреса электронной почты - с правильным анализатором - для вас:
require 'mail'
a = Mail::Address.new('[email protected]')
Это выдаст Mail::Field::ParseError
, если это не соответствующий адресу электронной почты. (Мы не занимаемся такими вещами, как поиск адресов MX или что-то в этом роде.)
Если вам нужен хороший опыт работы с валидатором Rails, вы можете сделать app/models/concerns/email_validatable.rb
:
require 'mail'
module EmailValidatable
extend ActiveSupport::Concern
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
begin
a = Mail::Address.new(value)
rescue Mail::Field::ParseError
record.errors[attribute] << (options[:message] || "is not an email")
end
end
end
end
и затем в своей модели вы можете:
include EmailValidatable
validates :email, email: true
Как упоминает ниже комментарий Иво Дзеччарова, он пропускает все, через что проходит действительный адрес "Кому:". Так что что-то вроде Foo Bar <[email protected]>
действительно. Это может быть проблемой для вас, это не может быть; В конце концов, это действительно правильный адрес.
Если вам нужна только адресная часть:
a = Mail::Address.new('Foo Bar <[email protected]>')
a.address
=> "[email protected]"
Как отмечает Бьорн Вайнбренне ниже, есть более допустимые адреса RFC2822, чем вы можете ожидать (я вполне уверен, что все перечисленные адреса соответствуют требованиям и могут получать почту в зависимости от конфигурации системы) - Вот почему я не рекомендую пробовать регулярные выражения, но использую совместимый парсер.
Если вам действительно важно, можете ли вы отправить электронное письмо на адрес, тогда лучше всего - на самом деле - отправить сообщение со ссылкой для подтверждения.
Если вы используете драгоценный камень Devise уже в своем приложении, возможно, будет удобно использовать
email =~ Devise.email_regexp
... что также означает, что разные места приложения используют ту же проверку.
@Nate Большое спасибо за то, что собрали этот ответ. Я не осознавал, что проверка электронной почты имела столько нюансов, пока не посмотрел на ваш фрагмент кода.
Я заметил, что текущая почта gem: mail-2.6.5 не выдает ошибку для письма "abc". Примеры:
>> a = Mail::Address.new('abc')
=> #<Mail::Address:70343701196060 Address: |abc| >
>> a.address # this is weird
=> "abc"
>> a = Mail::Address.new('"Jon Doe" <[email protected]>')
=> #<Mail::Address:70343691638900 Address: |Jon Doe <[email protected]>| >
>> a.address
=> "[email protected]"
>> a.display_name
=> "Jon Doe"
>> Mail::Address.new('"Jon Doe <jon')
Mail::Field::ParseError: Mail::AddressList can not parse |"Jon Doe <jon|
Reason was: Only able to parse up to "Jon Doe <jon
from (irb):3:in 'new'
from (irb):3
>>
Это выдает ошибки Mail::Field::ParseError
для "Jon Doe <jon
, и это здорово. Я верю, что проверим и простой "шаблон abc".
В app/models/concerns/pretty_email_validatable.rb
:
require 'mail'
module PrettyEmailValidatable
extend ActiveSupport::Concern
class PrettyEmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
begin
a = Mail::Address.new(value)
rescue Mail::Field::ParseError
record.errors[attribute] << (options[:message] || "is not an email")
end
# regexp from http://guides.rubyonrails.org/active_record_validations.html
value = a.address
unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
record.errors[attribute] << (options[:message] || "is not an email")
end
end
end
end
и затем в своей модели вы можете:
include PrettyEmailValidatable
validates :pretty_email, email: true
Таким образом, я использую вышеупомянутое для проверки "симпатичной электронной почты" и https://github.com/balexand/email_validator для стандартной проверки электронной почты.
Попробуйте validates_email_format_of gem.
Если кто-то еще очень сфокусирован на TDD: мне нужно что-то, что я мог бы написать тесты против и улучшить позже, если это необходимо, без привязки тестов к другой модели.
Создание Nate и tongueroo кода (спасибо Nate и tongueroo!), это было сделано в Rails 5
, Ruby 2.4.1
. Вот что я бросил в app/validators/email_validator.rb
:
require 'mail'
class EmailValidator < ActiveModel::EachValidator
def add_error(record, attribute)
record.errors.add(attribute, (options[:message] || "is not a valid email address"))
end
def validate_each(record, attribute, value)
begin
a = Mail::Address.new(value)
rescue Mail::Field::ParseError
add_error(record, attribute)
end
# regexp from http://guides.rubyonrails.org/active_record_validations.html
value = a.address unless a.nil?
unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
add_error(record, attribute)
end
end
end
И это ни в коем случае не является всеобъемлющим, но вот что я бросил в spec/validators/email_validator_spec.rb
:
require 'rails_helper'
RSpec.describe EmailValidator do
subject do
Class.new do
include ActiveModel::Validations
attr_accessor :email
validates :email, email: true
end.new
end
context 'when the email address is valid' do
let(:email) { Faker::Internet.email }
it 'allows the input' do
subject.email = email
expect(subject).to be_valid
end
end
context 'when the email address is invalid' do
let(:invalid_message) { 'is not a valid email address' }
it 'invalidates the input' do
subject.email = '[email protected]'
expect(subject).not_to be_valid
end
it 'alerts the consumer' do
subject.email = 'notvalid'
subject.valid?
expect(subject.errors[:email]).to include(invalid_message)
end
end
end
Надеюсь, что это поможет!
Ниже приведен новый способ проверки электронной почты:
validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, on: :create }
Обратитесь к Rails valications doc.
Простой ответ: Не используйте регулярное выражение. Слишком много краевых случаев и ложных негативов и ложных срабатываний. Проверьте знак @и отправьте письмо на адрес, чтобы проверить его:
VALID_EMAIL_REGEX = /\A[\w+\-.][email protected][a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
!("[email protected]" =~ VALID_EMAIL_REGEX).nil?
Вы можете попробовать это
VALID_EMAIL_REGEX = /\A[\w+\-.][email protected][a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
попробуйте это.
validates_format_of :email, :with => /^[\+A-Z0-9\._%-][email protected]([A-Z0-9-]+\.)+[A-Z]{2,4}$/i
Лучше всего следовать документации Rails:
https://guides.rubyonrails.org/active_record_validations.html#custom-validators
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 < ApplicationRecord
validates :email, presence: true, email: true
end
Пожалуйста, имейте в виду, что на данный момент email_validator гем не имеет сложных правил проверки, а только:
/[^\s]@[^\s]/
https://github.com/balexand/email_validator/blob/master/lib/email_validator.rb#L13
Аргументация в https://medium.com/hackernoon/the-100-correct-way-to-validate-email-addresses-7c4818f24643
В Ruby есть встроенное регулярное выражение для проверки писем:
validates :email, format: {
with: URI::MailTo::EMAIL_REGEXP,
message: 'Only valid emails allowed'
}