Je suis en train de valider mon email dans Rails avec:
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
En outre, je fais la validation HTML5 dans le frontend, mais les adresses e-mail comme
[email protected]
[email protected]
sont toujours valables. Qu'est-ce que je rate?
J'utilise la constante intégrée à l'URI dans la bibliothèque standard Ruby
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
N'utilisez pas d'expression régulière pour la validation d'adresse e-mail. C'est un piège. Il existe bien plus de formats d'adresses électroniques valides que vous ne le pensez. Pourtant! La gemme mail
(requise par ActionMailer, vous l'avez donc) analysera les adresses électroniques - avec un analyseur approprié - pour vous:
require 'mail'
a = Mail::Address.new('[email protected]')
Cela jettera un Mail::Field::ParseError
s'il s'agit d'une adresse électronique non conforme. (Nous n'entrons pas dans des choses comme faire une recherche d'adresse MX ou quoi que ce soit.)
Si vous voulez la bonne expérience Rails, vous pouvez faire 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
et ensuite dans votre modèle, vous pouvez:
include EmailValidatable
validates :email, email: true
Comme le commentaire ci-dessous d'Iwo Dziechciarow le mentionne, tout ce qui est une adresse valide "A:" est transmis. Donc, quelque chose comme Foo Bar <[email protected]>
est valable. Cela pourrait être un problème pour vous, peut-être pas; c'est vraiment est une adresse valide, après tout.
Si vous ne voulez que l’adresse:
a = Mail::Address.new('Foo Bar <[email protected]>')
a.address
=> "[email protected]"
Comme Björn Weinbrenne le note ci-dessous, il existe des solutions beaucoup plus valables adresses RFC2822 que vous ne le pensez probablement (toutes les adresses répertoriées ici sont conformes et peuvent recevoir du courrier en fonction des configurations système). pourquoi je ne recommande pas d'essayer une expression rationnelle, mais d'utiliser un analyseur conforme.
Si vous vous souciez vraiment de savoir si vous pouvez envoyer un courrier électronique à une adresse, alors votre meilleur choix - de loin - consiste à envoyer un message avec un lien de vérification.
Si vous utilisez déjà la gemme Devise dans votre application, il peut être opportun d'utiliser
email =~ Devise.email_regexp
... ce qui signifie également que différents endroits de l'application utilisent la même validation.
@Nate Merci beaucoup d'avoir préparé cette réponse. Je ne savais pas que la validation des e-mails comportait autant de nuances avant de consulter votre extrait de code.
J'ai remarqué que le joyau actuel du courrier: mail-2.6.5 ne génère pas d'erreur pour un email de "abc". Exemples:
>> 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
>>
Il jette Mail::Field::ParseError
_ erreurs pour "Jon Doe <jon
qui est super. Je crois que va vérifier pour le simple "modèle abc" aussi.
Dans 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
et ensuite dans votre modèle, vous pouvez:
include PrettyEmailValidatable
validates :pretty_email, email: true
J'utilise donc ce qui précède pour la validation du "joli courrier électronique" et le https://github.com/balexand/email_validator pour la validation du courrier électronique standard.
Essayez validates_email_format_of gem.
Si quelqu'un d'autre est très concentré sur le TDD: je voulais quelque chose pour lequel je pourrais écrire des tests et que je pourrais améliorer plus tard si nécessaire, sans lier les tests à un autre modèle.
Construit à partir du code de Nate et tongueroo (Merci Nate et tongueroo !), Cela a été fait en Rails 5
, Ruby 2.4.1
. Voici ce que j'ai jeté dans 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
Et ceci n’est nullement exhaustif, mais voici ce que j’ai jeté dans 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
J'espère que ça aide!
Voici la nouvelle façon Rails de faire la validation de courrier électronique:
validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, on: :create }
Reportez-vous à la documentation sur les valises Rails .
La réponse simple est: n'utilisez pas une expression rationnelle. Il y a trop de cas Edge, de faux négatifs et de faux positifs. Recherchez un signe @ et envoyez un mail à l'adresse pour le valider:
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
!("[email protected]" =~ VALID_EMAIL_REGEX).nil?
Ruby a une expression régulière intégrée pour valider les emails:
validates :email, format: {
with: URI::MailTo::EMAIL_REGEXP,
message: 'Only valid emails allowed'
}
Tu peux essayer ça
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
Est préférable de suivre Rails documentation:
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
Sachez que pour l'instant email_validator gem n'a pas de règles de validation complexes, mais seulement:
/[^\s]@[^\s]/
https://github.com/balexand/email_validator/blob/master/lib/email_validator.rb#L1
L'argumentation est dans https://medium.com/hackernoon/the-100-correct-way-to-validate-email-addresses-7c4818f2464
essaye ça.
validates_format_of :email, :with => /^[\+A-Z0-9\._%-]+@([A-Z0-9-]+\.)+[A-Z]{2,4}$/i