Railsでメールアドレスをバリデーションする方法(翻訳)
RailsのActive Recordは、意味のあるデータを確実に得られるようにするためのバリデーション機能をActive Modelライブラリ経由で提供しています。
ユーザーにメールを送信可能であることを確認する処理は、アプリケーションを正しく設定するうえで非常に重要なので、既に皆さんもUser#email
属性でバリデーションを行っていることでしょう。
🔗 以下の方法ではなく
手作り正規表現や、Railsの古いAPIドキュメントにあるものを使う。
class User < ApplicationRecord
validates :email,
format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i },
presence: true,
uniqueness: { case_insensitive: true }
end
🔗 以下の方法を使うこと
以下のより良いオプションの中から選ぶ。
🔗 Deviseの機能を利用する
既にDeviseでユーザー認証を行っているのであれば、Deviseのメールバリデーション機能を使うのが合理的です。
class User < ApplicationRecord
validates :email,
format: { with: Devise.email_regexp },
presence: true,
uniqueness: { case_insensitive: true }
end
🔗 URI
ライブラリ
Ruby 2.2以降では、Ruby標準のURL
ライブラリにメールアドレスバリデーション用の正規表現が組み込まれています(追加されたときのコミット: e63ab5d)。
class User < ApplicationRecord
validates :email,
format: { with: URI::MailTo::EMAIL_REGEXP },
presence: true,
uniqueness: { case_insensitive: true }
end
この書き方はRailsガイドのサンプルコードでも使われています。
🔗 email_address
gem
email_address gemのバリデーション機能には、メールアドレスを扱ううえで便利な拡張が他にもあります。
組み込みのバリデータを使う場合は以下のように書きます。
class User < ApplicationRecord
validates :email,
presence: true,
uniqueness: { case_insensitive: true }
# 属性名は`:email`にする必要がある
validates_with EmailAddress::ActiveRecordValidator
end
validate
ブロック内でemail_address gemの機能を直接利用することもできます。
class User < ApplicationRecord
validates :email,
presence: true,
uniqueness: { case_insensitive: true }
validate do |user|
EmailAddress::Address.new(user.email).valid?
end
end
Railsのカスタムバリデータを利用する方法もあります。
class User < ApplicationRecord
validates :email,
email: true,
presence: true,
uniqueness: { case_insensitive: true }
private
# 他の場所でもメールの有効性を確認したい場合は別ファイルに置くことも可能
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless EmailAddress::Address.new(value).valid?
record.errors.add attribute, (options[:message] || "is not an email")
end
end
end
end
🔗 そうする理由
メールアドレスのフォーマットの問題はとっくに解決済みです。既に解決した問題を「また」解決しようとするのは、見ていて頭がおかしくなりそうです。これと同じぐらい一般的な問題を解決するときは、インターネット全体で積み重ねられてきた経験に頼らない手はないでしょう。
上述したさまざまな良いオプションの背後では正規表現が利用されており、その厳密性のレベルはオプションごとに異なるものの、ほとんどのオプションは、公式RFCの厳密すぎる形式(email_address gemのREADMEをお読みください)に沿ったメールアドレスと比べれば「健全な慣習」レベルにあります。なお、公式RFCとして有効な、機械的に生成された(現実にはまずなさそうな)メールアドレスがバリデーションをパスする可能性もあります。
私が上のより良いオプションのどれを選ぶかと尋ねられたら、シンプルなURI
ライブラリを選ぶでしょう。このライブラリの背後で使われている正規表現は、通常の利用であれば十分柔軟です。
自分のアプリケーションでメールアドレスに「検出」「正規化」「部分の取り出し」操作を追加する必要が生じたらemail_address gemを採用し、ついでにバリデーションにも使いたくなるでしょう。
メールアドレスのバリデーションをアプリケーション内の複数箇所で行うのであれば、再利用可能なカスタムバリデータを検討するのは確実でしょう。
🔗 そうしない理由があるとすれば
上述のよりよいオプションで提供されるバリデーションの厳密さのレベル(および背後の正規表現)はそれぞれ異なるので、既存アプリでこれまで手作り正規表現などでバリデーションしていたものから乗り換えてバリデーションの仕様が変わると、もしかしたら問題が生じることがあるかもしれません。
しかし手作り正規表現を使い続けるよりも、新しいオプションに乗り換えて、アプリが扱うメールアドレスがその正規表現のバリデーションですべてパスするように作業する方が「おそらく」価値があるでしょう。
新規アプリであれば、従来のユースケースから大きく外れる非常に特殊なメールアドレスを扱うことが要求される場合でもない限り、手作りよりも上述の適切なソリューションの中から選ぶようにしましょう。
🔗 baba(BPS CTO)よりコメント
URI
ライブラリの正規表現は、WHATWGの正規表現をそのまま使っています(正規表現冒頭の^
と末尾の$
をRubyで安全な\A
と\z
に変えている以外は同じです)。
# https://github.com/ruby/ruby/commit/e63ab5d3ad289767eab49787e4e33390b0ce74e1#diff-61fb6899fba73f4d989b5f0d0c3be6a6a993c7a994c2c1c79f67b1cb46ed4a67R54
# http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#valid-e-mail-address
EMAIL_REGEXP = /\A[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\z/
これにより、URIライブラリのメールアドレスバリデーションがブラウザのデフォルトのinput type="email"
バリデーションと一致するという大きなメリットを得られます。
参考: <input type="email">
- HTML: ハイパーテキストマークアップ言語 | MDN
- Deviseのメールバリデーションでは、最新のDevise v4.9.3でも以下のような非常にユルい正規表現が使われていることは知っておいてよいでしょう。それこそ
ほげ@ぴよ
でもバリデーションがパスするレベルです。
# https://github.com/heartcombo/devise/blob/v4.9.3/lib/devise.rb#L118
@@email_regexp = /\A[^@\s]+@[^@\s]+\z/
概要
原著者の許諾を得て翻訳・公開いたします。
日本語タイトルは内容に即したものにしました。
記事末尾のコメントもぜひお読みください。