Como fazer a validação de E-mail em Ruby on Rails?
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
também faço a validação do HTML5 na interface, mas endereços de E-mail como
[email protected]
[email protected]
ainda são válidos. O que me está a escapar?
14 answers
Uso a constante incorporada no URI na biblioteca ruby normal
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
Actualização: acabei de encontrar a valid_ Email2 gem que parece muito boa.
Não use uma expressão regular para a validação do endereço de E-mail. É uma armadilha. Há muito mais formatos de endereço de E-mail válidos do que você vai pensar. No entanto! A gema (é necessária pelo ActionMailer, por isso tem-na) irá processar os endereços de E-mail-com um analisador adequado-para si:
require 'mail'
a = Mail::Address.new('[email protected]')
Isto vai lançar um Mail::Field::ParseError
Se for um endereço de E-mail não conforme. (Nós não estamos nos metendo em coisas como a fazer uma pesquisa de endereços MX ou algo assim.)
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
E depois no teu modelo, podes:
include EmailValidatable
validates :email, email: true
Como o comentário de Iwo Dziechciarow menciona abaixo, Isto passa qualquer coisa que seja um endereço válido "para:". Então algo como Foo Bar <[email protected]>
é válido. Este pode ser um problema para você, pode não ser; ele realmente é um endereço válido, afinal de contas.
Se quiser apenas a parte do endereço de it:
a = Mail::Address.new('Foo Bar <[email protected]>')
a.address
=> "[email protected]"
Como Björn Weinbrenne notas abaixo, há uma maneira mais válido RFC2822 endereços que você pode esperar (tenho certeza de que todos os endereços listados não são compatíveis, e pode receber mail de acordo configurações do sistema) - é por isso que eu não recomendo tentar uma regex, mas usando um analisador compatível com.
Se realmente se importa se pode enviar e-mail para um endereço, então a sua melhor aposta-de longe-é realmente enviar uma mensagem com uma verificação link.
Se usar a gema do dispositivo já no seu aplicativo, pode ser oportuno usar
email =~ Devise.email_regexp
...o que também significa que diferentes locais da aplicação usam a mesma validação.
Tentar validates_ email_ format_ of gem.
Eu notei que a gema do correio atual: mail-2.6.5 não lança um erro para um e-mail de "abc". Exemplos:
>> 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
>>
Ele atira Mail::Field::ParseError
erros para "Jon Doe <jon
o que é óptimo. Eu acredito que vai verificar para o simples "padrão abc" também.
Em 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
E depois no teu modelo, tu... can:
include PrettyEmailValidatable
validates :pretty_email, email: true
Então eu uso o acima para a validação do "pretty email" e o https://github.com/balexand/email_validator para validação padrão de E-mail.
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
Edifício fora de Nate e tongueroo's código (Graças Nate e tongueroo!), isto foi feito em Rails 5
, Ruby 2.4.1
. Aqui está o que eu coloquei 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
E isto não é nada abrangente, mas aqui está o que eu coloquei 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 = 'not_valid@'
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
Espero que sim. ajuda!
validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, on: :create }
Ver as valicações dos carris doc.
A resposta simples é: não uses uma expressão regular. Há demasiados casos extremos e falsos negativos e falsos positivos. Verifique se existe um sinal @ e envie um e-mail para o endereço para validá-lo:
É melhor seguir a documentação dos carris:
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
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
!("[email protected]" =~ VALID_EMAIL_REGEX).nil?
validates_format_of :email, :with => /^[\+A-Z0-9\._%-]+@([A-Z0-9-]+\.)+[A-Z]{2,4}$/i
Existe validates_email_ format_of a biblioteca ruby que valida os endereços de E-mail com base no RFC 2822 e no RFC 3696
Utilização:
Adicione ao seu ficheiro Gemfile:
gem 'validates_email_format_of'
Na tua classe de modelos
class Person < ActiveRecord::Base
validates :email, email_format: { message: 'is not looking good' }
end
O Ruby tem uma expressão regular incorporada para validar e-mails:
validates :email, format: {
with: URI::MailTo::EMAIL_REGEXP,
message: 'Only valid emails allowed'
}