ユーザーのメールアドレスを検証するために何を使用していますか?その理由は何ですか?
私は validates_email_veracity_of
実際にMXサーバーを照会します。しかし、主にネットワークトラフィックと信頼性に関連するさまざまな理由で失敗に満ちています。
私は周りを見回しましたが、多くの人々が電子メールアドレスの健全性チェックを実行するために使用していることは明らかでした。このために維持された、かなり正確なプラグインまたは宝石はありますか?
追伸:メールが機能するかどうかを確認するためのリンクを記載したメールを送信しないでください。 「友人に送信」機能を開発しているため、これは実用的ではありません。
Rails 3.0では、 Mail gem を使用して、正規表現なしで電子メール検証を使用できます。
私の実装 ( gemとしてパッケージ化 )です。
これを必要以上に難しくしないでください。機能は重要ではありません。検証は、タイプミスを検出するための基本的な正気のステップです。私は単純な正規表現でそれを行い、あまりにも複雑なものにCPUサイクルを浪費しません:
/\A[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+\z/
これは http://www.regular-expressions.info/email.html から変更されたもので、実際にすべてのトレードオフを知りたい場合に読む必要があります。 RFC822に準拠した、より正確ではるかに複雑な正規表現が必要な場合は、そのページにも記載されています。しかし、問題はこれです:完全に正しくする必要はありません。
アドレスが検証に合格した場合、メールを送信します。電子メールが失敗すると、エラーメッセージが表示されます。その時点でユーザーに伝えることができます「申し訳ありませんが、あなたの友人はそれを受け取りませんでした。もう一度やりますか?」または手動でフラグを立てます確認するか、単に無視するか、何でも。
これらは、アドレスdidが検証に合格した場合に対処しなければならないオプションと同じです。検証が完璧で、アドレスが存在するという絶対的な証拠を取得したとしても、送信が失敗する可能性があるためです。
検証の誤検知のコストは低いです。より良い検証の利点も低いです。寛大に検証し、エラーが発生したときに心配します。
Rails 3で電子メール検証用のgemを作成しました。Railsにデフォルトでこのようなものが含まれていないことに少し驚いています。
このプロジェクトは、現時点では最も多くのウォッチャーがgithubにいるようです(Railsでの電子メール検証用)。
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
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
これにより、tagged "[email protected]"などの電子メールなど、あらゆる種類の有効な電子メールが許可されます。
spec/validators/email_validator_spec.rb
のrspec
でこれをテストするには
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では、reusableバリデーターを書くことができます。
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
Hallelujah が示唆するように、 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 (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がポップアップするときにリストを更新する必要があります(2012年の追加.mobi
および.tel
)
パーサーを直接フックする利点は、Mail gemが使用する部分に対して Mail grammarのルール がかなり広いことです。これは、user<[email protected]>
などのアドレスを解析できるように設計されています。 SMTPに共通。 Mail::Address
からそれを消費することで、余分なチェックをたくさん行わなければなりません。
Mail gemに関する別の注意点は、クラスがRFC2822と呼ばれていても、文法には RFC5322 の要素がいくつかあります。たとえば this test です。
他の答えに注目すると、問題はまだ残っています-なぜそれについて気になるのですか?
多くの正規表現が拒否または見逃す可能性のあるEdgeケースの実際の量には問題があるようです。
質問は「何を達成しようとしているのですか?」だと思います。たとえメールアドレスを「検証」しても、実際に有効なメールアドレスであることを検証しているわけではありません。
正規表現を使用する場合は、クライアント側で@の存在を確認してください。
誤った電子メールのシナリオについては、「メッセージの送信に失敗しました」ブランチをコードに追加してください。
Mail gemにはアドレスパーサーが組み込まれています。
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._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+\z/.match(value).nil?
false
else
true
end
end
end
メーリングリスト検証 (Rails 4.1.6)を使用します)
here から正規表現を取得しました。これは非常に完全なもののようで、多数の組み合わせに対してテストされています。そのページで結果を見ることができます。
私はそれをRuby regexpに少し変更し、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
、それは非常に単純な正規表現であり、必要なすべてのケースを取得できませんでした。いくつかのメールがぶつかりました。
私はウェブから他の正規表現を試しましたが、これは今までで最高の結果を得ました。それがあなたのケースに役立つことを願っています。
基本的に3つの最も一般的なオプションがあります。
Validates_email_veracity_ofとトークン生成の両方を使用したくない場合は、旧式の正規表現チェックを使用します。