web-dev-qa-db-ja.com

Rails上のRubyでメール検証を行う方法は?

私はRailsでメール検証を行っています:

validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i

また、フロントエンドでHTML5検証を行いますが、メールアドレスは

[email protected]
[email protected]

まだ有効です。私は何が欠けていますか?

42
Haseeb Ahmad

標準のRubyライブラリのURIに組み込まれた定数を使用します

validates :email, format: { with: URI::MailTo::EMAIL_REGEXP } 
116
Joshua Hunter

更新: valid_email2 gemが見つかりました。

メールアドレスの検証に正規表現を使用しないでください。それは罠だ。あなたが考えるよりもはるかに有効なメールアドレス形式があります。しかしながら! mail gem(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

以下のIwo Dziechciarowのコメントが言及しているように、これは有効な「To:」アドレスであるものをすべて渡します。したがって、Foo Bar <[email protected]>のようなものが有効です。これはあなたにとって問題かもしれませんが、そうでないかもしれません。本当に有効なアドレスです。

アドレス部分だけが必要な場合:

a = Mail::Address.new('Foo Bar <[email protected]>')
a.address
=> "[email protected]"

BjörnWeinbrenneが以下に指摘しているように、 RFC2822アドレス は予想よりもはるかに有効です(そこにリストされているアドレスはすべて準拠していると確信しており、システム構成に応じてメールを受信します)—これは正規表現を試すことをお勧めしないが、準拠したパーサーを使用する理由。

メールをアドレスに送信できるかどうかを本当に気にする場合、最善の策は、間違いなく検証リンク付きのメッセージを送信することです

19
Nate

既にアプリでDevise gemを使用している場合は、使用するのが適切かもしれません

email =~ Devise.email_regexp

...これは、アプリの異なる場所で同じ検証が使用されることも意味します。

13
Martin T.

@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 を使用します。

7
tongueroo

validates_email_format_of gemを試してください。

5
thaleshcv

他の誰かが非常にTDDに焦点を合わせている場合:テストを別のモデルに結び付けずに、必要に応じてテストを作成し、後で改善できるものが必要でした。

Nate および tongueroo のコードの構築(ありがとう Nate および tongueroo !)、これはRails 5で行われました、Ruby 2.4.1app/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 = '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

それが役に立てば幸い!

4
Jack Barry

以下に、メールの検証を行う新しいRailsの方法を示します。

validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, on: :create }

Rails valications doc を参照してください。

3
Cyzanfar

簡単な答えは次のとおりです。正規表現を使用しないでください。 Edgeのケースが多すぎて、偽陰性と偽陽性があります。 @記号を確認し、そのアドレスにメールを送信して検証します。

https://www.youtube.com/watch?v=xxX81WmXjPg

2
Martin T.
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
!("[email protected]" =~ VALID_EMAIL_REGEX).nil?
2
A H K

Rubyには、メールを検証するための正規表現が組み込まれています。

validates :email, format: {
    with: URI::MailTo::EMAIL_REGEXP,
    message: 'Only valid emails allowed'
  }

ソース: Ruby標準ライブラリ:URI :: MailTo

1
Will

これを試すことができます

VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
1
vipin cp

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
1
Adriano Tadao

現時点ではemail_validatorgemには複雑な検証ルールはありませんが、

/[^\s]@[^\s]/

https://github.com/balexand/email_validator/blob/master/lib/email_validator.rb#L1

引数は https://medium.com/hackernoon/the-100-correct-way-to-validate-email-addresses-7c4818f2464 にあります

1
gayavat

これを試して。

validates_format_of  :email, :with => /^[\+A-Z0-9\._%-]+@([A-Z0-9-]+\.)+[A-Z]{2,4}$/i
1
jithya